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.

1318 lines
35 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. userenum.c
  5. Abstract:
  6. This module implements a pair of user enumeration functions to consolidate
  7. general-case and special-case processing of users. The caller does not
  8. need to know how a machine's user profiles are configured because the
  9. code here abstracts the details.
  10. The caller gets:
  11. - Each user name, .default for the logon prompt, and Default User for the
  12. NT default user account
  13. - The Win9x user.dat location for each user, including the default user
  14. - The Win9x profile directory, or All Users for the default user
  15. - The symbolic NT profile directory
  16. - The account type (normal, administrator and/or default)
  17. - Indication that the account registry is valid
  18. - Indication that the account is the current logged-on user or last logged-on
  19. user
  20. Routines:
  21. EnumFirstUser - Begins the user enumeration
  22. EnumNextUser - Continues the user enumeration
  23. EnumUserAbort - Cleans up an enumeration that did not complete
  24. Author:
  25. Jim Schmidt (jimschm) 23-Jul-1997
  26. Revision History:
  27. Jim Schmidt (jimschm) 08-Sep-1998 Changed to a better state machine to
  28. clean up the evolved complexity
  29. Jim Schmidt (jimschm) 09-Jun-1998 Revisions for dynamic user profile dir
  30. --*/
  31. #include "pch.h"
  32. #include "cmn9xp.h"
  33. #define DBG_USERENUM "UserEnum"
  34. #define UE_INITIALIZED 0x0001
  35. #define UE_SF_COLLISIONS 0x0002
  36. static DWORD g_UserEnumFlags = 0;
  37. VOID
  38. pMoveAndRenameProfiles (
  39. IN PCTSTR ProfileList
  40. )
  41. /*++
  42. Routine Description:
  43. pReportNonMigrateableUserAccounts adds a message to the incompatibility
  44. report when a condition that makes user migration impossible (except current user)
  45. is detected
  46. Arguments:
  47. ProfileList - Specifies the list of non-migrated user profile paths (multisz)
  48. Return Value:
  49. none
  50. --*/
  51. {
  52. MULTISZ_ENUM msze;
  53. PTSTR p, append;
  54. TCHAR sourceDir[MAX_TCHAR_PATH];
  55. TREE_ENUM e;
  56. TCHAR newDest[MAX_TCHAR_PATH];
  57. PTSTR profiles;
  58. TCHAR tempFile[MAX_TCHAR_PATH];
  59. profiles = JoinPaths (g_WinDir, TEXT("Profiles"));
  60. if (EnumFirstMultiSz (&msze, ProfileList)) {
  61. do {
  62. //
  63. // remove user.dat from the path
  64. //
  65. StackStringCopy (sourceDir, msze.CurrentString);
  66. p = _tcsrchr (sourceDir, TEXT('\\'));
  67. if (!p) {
  68. MYASSERT (FALSE);
  69. continue;
  70. }
  71. *p = 0;
  72. MYASSERT (StringIMatch (p + 1, TEXT("user.dat")));
  73. p = _tcsrchr (sourceDir, TEXT('\\'));
  74. if (!p) {
  75. MYASSERT (FALSE);
  76. continue;
  77. }
  78. //
  79. // append Win9x OS name to the target directory name
  80. //
  81. append = newDest + wsprintf (newDest, TEXT("%s%s.%s"), g_ProfileDirNt, p, g_Win95Name);
  82. if (CanSetOperation (sourceDir, OPERATION_FILE_MOVE_EXTERNAL)) {
  83. MarkFileForMoveExternal (sourceDir, newDest);
  84. }
  85. *append = TEXT('\\');
  86. append++;
  87. //
  88. // now enumerate and move all the files
  89. //
  90. if (StringIPrefix (sourceDir, profiles) && EnumFirstFileInTree (&e, sourceDir, NULL, TRUE)) {
  91. do {
  92. StringCopy (append, e.SubPath);
  93. if (!e.Directory) {
  94. //
  95. // remove old operation and set a new one
  96. // with the updated final dest
  97. //
  98. if (CanSetOperation (e.FullPath, OPERATION_TEMP_PATH)) {
  99. ComputeTemporaryPath (e.FullPath, NULL, NULL, g_TempDir, tempFile);
  100. MarkFileForTemporaryMoveEx (e.FullPath, newDest, tempFile, TRUE);
  101. }
  102. } else {
  103. if (CanSetOperation (e.FullPath, OPERATION_FILE_MOVE_EXTERNAL)) {
  104. MarkFileForMoveExternal (e.FullPath, newDest);
  105. }
  106. }
  107. } while (EnumNextFileInTree (&e));
  108. }
  109. } while (EnumNextMultiSz (&msze));
  110. }
  111. FreePathString (profiles);
  112. }
  113. VOID
  114. pReportNonMigrateableUserAccounts (
  115. IN PCTSTR UserList
  116. )
  117. /*++
  118. Routine Description:
  119. pReportNonMigrateableUserAccounts adds a message to the incompatibility
  120. report when a condition that makes user migration impossible (except current user)
  121. is detected
  122. Arguments:
  123. UserList - Specifies the list of non-migrated users (multisz)
  124. Return Value:
  125. none
  126. --*/
  127. {
  128. PCTSTR MsgGroup = NULL;
  129. PCTSTR RootGroup = NULL;
  130. PCTSTR SubGroup = NULL;
  131. PCTSTR Message = NULL;
  132. PCTSTR ArgArray[2];
  133. MULTISZ_ENUM msze;
  134. __try {
  135. RootGroup = GetStringResource (MSG_LOSTSETTINGS_ROOT);
  136. SubGroup = GetStringResource (MSG_SHARED_USER_ACCOUNTS);
  137. if (!RootGroup || !SubGroup) {
  138. MYASSERT (FALSE);
  139. __leave;
  140. }
  141. //
  142. // Build "Settings That Will Not Be Upgraded\Shared User Accounts"
  143. //
  144. MsgGroup = JoinPaths (RootGroup, SubGroup);
  145. //
  146. // Send message to report
  147. //
  148. ArgArray[0] = g_Win95Name;
  149. ArgArray[1] = g_ProfileDirNt;
  150. Message = ParseMessageID (MSG_SHARED_USER_ACCOUNTS_MESSAGE, ArgArray);
  151. if (Message) {
  152. MsgMgr_ObjectMsg_Add (TEXT("*SharedUserAccounts"), MsgGroup, Message);
  153. }
  154. if (EnumFirstMultiSz (&msze, UserList)) {
  155. do {
  156. //
  157. // remove all associated messages from the report
  158. //
  159. HandleObject (msze.CurrentString, TEXT("UserName"));
  160. } while (EnumNextMultiSz (&msze));
  161. }
  162. }
  163. __finally {
  164. //
  165. // Clean up
  166. //
  167. FreeStringResource (Message);
  168. FreeStringResource (RootGroup);
  169. FreeStringResource (SubGroup);
  170. FreePathString (MsgGroup);
  171. }
  172. }
  173. VOID
  174. pCheckShellFoldersCollision (
  175. VOID
  176. )
  177. {
  178. USERENUM e;
  179. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  180. PCTSTR idShellFolder;
  181. PCTSTR path;
  182. GROWBUFFER gb = GROWBUF_INIT;
  183. GROWBUFFER users = GROWBUF_INIT;
  184. GROWBUFFER profilesWin9x = GROWBUF_INIT;
  185. MULTISZ_ENUM msze;
  186. TCHAR key[MEMDB_MAX];
  187. BOOL collisions = FALSE;
  188. if (EnumFirstUser (&e, 0)) {
  189. if (!e.CommonProfilesEnabled) {
  190. if (InfFindFirstLine (g_Win95UpgInf, S_PROFILES_SF_COLLISIONS, NULL, &is)) {
  191. do {
  192. idShellFolder = InfGetStringField (&is, 1);
  193. if (idShellFolder && *idShellFolder) {
  194. MultiSzAppend (&gb, idShellFolder);
  195. }
  196. } while (InfFindNextLine (&is));
  197. InfCleanUpInfStruct (&is);
  198. }
  199. do {
  200. if (!EnumFirstMultiSz (&msze, (PCTSTR)gb.Buf)) {
  201. break;
  202. }
  203. if (!(e.AccountType & (LOGON_PROMPT | DEFAULT_USER | INVALID_ACCOUNT))) {
  204. if (!(e.AccountType & CURRENT_USER)) {
  205. MultiSzAppend (&users, e.UserName);
  206. MultiSzAppend (&profilesWin9x, e.UserDatPath);
  207. }
  208. if (!collisions) {
  209. do {
  210. path = ShellFolderGetPath (&e, msze.CurrentString);
  211. if (path) {
  212. MemDbBuildKey (key, MEMDB_CATEGORY_PROFILES_SF_COLLISIONS, msze.CurrentString, path, NULL);
  213. if (MemDbGetValue (key, NULL)) {
  214. //
  215. // this shell folder path is shared between multiple users
  216. //
  217. LOG ((
  218. LOG_INFORMATION,
  219. "User %s shares path %s with another user for %s",
  220. e.UserName,
  221. path,
  222. msze.CurrentString
  223. ));
  224. collisions = TRUE;
  225. break;
  226. }
  227. LOG ((
  228. LOG_INFORMATION,
  229. "User %s uses path %s for %s",
  230. e.UserName,
  231. path,
  232. msze.CurrentString
  233. ));
  234. MemDbSetValue (key, 0);
  235. FreePathString (path);
  236. }
  237. } while (EnumNextMultiSz (&msze));
  238. }
  239. }
  240. } while (EnumNextUser (&e));
  241. }
  242. EnumUserAbort (&e);
  243. }
  244. if (collisions) {
  245. //
  246. // show this in the upgrade report
  247. //
  248. LOG ((
  249. LOG_WARNING,
  250. "Some user profiles share special shell folders; only the current account will be migrated"
  251. ));
  252. MYASSERT (users.Buf && profilesWin9x.Buf);
  253. pReportNonMigrateableUserAccounts (users.Buf);
  254. //
  255. // rename their profile from <Profiles9x>\<username> to <ProfilesNT>\<username>.<Win9xOSName>
  256. //
  257. pMoveAndRenameProfiles (profilesWin9x.Buf);
  258. //
  259. // set the global flag
  260. //
  261. g_UserEnumFlags |= UE_SF_COLLISIONS;
  262. }
  263. FreeGrowBuffer (&gb);
  264. FreeGrowBuffer (&users);
  265. MemDbDeleteTree (MEMDB_CATEGORY_PROFILES_SF_COLLISIONS);
  266. }
  267. BOOL
  268. pUserMigrationDisabled (
  269. IN PUSERENUM EnumPtr
  270. )
  271. {
  272. return (g_UserEnumFlags & UE_SF_COLLISIONS) != 0 &&
  273. !(EnumPtr->AccountType & (CURRENT_USER | DEFAULT_USER));
  274. }
  275. BOOL
  276. pIsProfileDirInUse (
  277. IN PVOID ProfileDirTable,
  278. IN PCTSTR ProfileDirName,
  279. IN PCTSTR ActualUserName
  280. )
  281. {
  282. LONG rc;
  283. if (StringIMatch (ProfileDirName, ActualUserName)) {
  284. return FALSE;
  285. }
  286. rc = pSetupStringTableLookUpString (
  287. ProfileDirTable,
  288. (PTSTR) ProfileDirName,
  289. STRTAB_CASE_INSENSITIVE
  290. );
  291. if (rc != -1) {
  292. return TRUE;
  293. }
  294. if (StringIMatch (ProfileDirName, g_AdministratorStr)) {
  295. return TRUE;
  296. }
  297. if (StringIMatch (ProfileDirName, S_DEFAULT_USER)) {
  298. return TRUE;
  299. }
  300. if (StringIMatch (ProfileDirName, S_ALL_USERS)) {
  301. return TRUE;
  302. }
  303. if (StringIMatch (ProfileDirName, S_LOCALSERVICE_USER)) {
  304. return TRUE;
  305. }
  306. if (StringIMatch (ProfileDirName, S_NETWORKSERVICE_USER)) {
  307. return TRUE;
  308. }
  309. return FALSE;
  310. }
  311. BOOL
  312. pIsAdministratorUserName (
  313. IN PCTSTR UserName
  314. )
  315. /*++
  316. Routine Description:
  317. Determines if the specified name is the administrator account or not.
  318. Arguments:
  319. UserName - Specifies the user name (without a domain name)
  320. Return Value:
  321. TRUE if the specified string is the same as "Administrator"
  322. FALSE if the specified string is not "Administrator"
  323. --*/
  324. {
  325. return StringIMatch (UserName, g_AdministratorStr);
  326. }
  327. VOID
  328. pPrepareStructForNextUser (
  329. IN OUT PUSERENUM EnumPtr
  330. )
  331. /*++
  332. Routine Description:
  333. pPrepareStructForNextUser initializes the user-specific members of the enum
  334. struct.
  335. Arguments:
  336. EnumPtr - Specifies the previous enum state, receives the initialized enum
  337. state.
  338. Return Value:
  339. None.
  340. --*/
  341. {
  342. //
  343. // Init flags
  344. //
  345. EnumPtr->DefaultUserHive = FALSE;
  346. EnumPtr->CreateAccountOnly = FALSE;
  347. //
  348. // Init names
  349. //
  350. EnumPtr->UserName[0] = 0;
  351. EnumPtr->FixedUserName[0] = 0;
  352. // AdminUserName is the true Win9x user name of the future Administrator
  353. EnumPtr->AdminUserName[0] = 0;
  354. EnumPtr->FixedAdminUserName[0] = 0;
  355. //
  356. // Init paths
  357. //
  358. EnumPtr->UserDatPath[0] = 0;
  359. EnumPtr->ProfileDirName[0] = 0;
  360. EnumPtr->OrgProfilePath[0] = 0;
  361. EnumPtr->NewProfilePath[0] = 0;
  362. //
  363. // Init values
  364. //
  365. EnumPtr->AccountType = 0;
  366. //
  367. // Init reg value
  368. //
  369. if (EnumPtr->UserRegKey) {
  370. CloseRegKey (EnumPtr->UserRegKey);
  371. EnumPtr->UserRegKey = NULL;
  372. }
  373. }
  374. VOID
  375. pPrepareStructForReturn (
  376. IN OUT PUSERENUM EnumPtr,
  377. IN ACCOUNTTYPE AccountType,
  378. IN USERENUM_STATE NextState
  379. )
  380. /*++
  381. Routine Description:
  382. pPrepareStructForReturn performs processing common to any type of
  383. enumeration. This includes:
  384. - Identifying an actual Win9x user named Administrator (a special case)
  385. - Finding the fixed name (i.e., NT-compatible name) for the user account
  386. - Mapping in the hive into the registry
  387. - Computing the full path to the profile directory, as well as the profile
  388. dir name (i.e., joeuser.001). The profile dir is encoded as >username
  389. because we don't know the true location until GUI mode.
  390. - Setting flags for current user or last logged on user
  391. The caller must set UserName and DefaultUserHive prior to calling
  392. this function (as well as all enumeration-wide members such as current
  393. user name).
  394. Arguments:
  395. EnumPtr - Specifies the partially completed enum state. Receives the
  396. complete enum state.
  397. AccountType - Specifies the account type being returned.
  398. NextState - Specifies the next state for the state machine, used when the
  399. caller calls EnumNextUser.
  400. Return Value:
  401. None.
  402. --*/
  403. {
  404. DWORD rc;
  405. PTSTR p;
  406. TCHAR TempDir[MAX_TCHAR_PATH];
  407. UINT TempDirSeq;
  408. HKEY key;
  409. HKEY userKey;
  410. PCTSTR data;
  411. //
  412. // Fill in state machine members
  413. //
  414. EnumPtr->AccountType = AccountType;
  415. EnumPtr->State = UE_STATE_RETURN;
  416. EnumPtr->NextState = NextState;
  417. //
  418. // Check if named user is also Administrator
  419. //
  420. if (AccountType & NAMED_USER) {
  421. if (pIsAdministratorUserName (EnumPtr->UserName)) {
  422. EnumPtr->AccountType |= ADMINISTRATOR;
  423. StringCopy (EnumPtr->AdminUserName, EnumPtr->UserName);
  424. }
  425. //
  426. // If this is a named user but there is no hive, use the default hive
  427. //
  428. key = OpenRegKeyStr (S_HKLM_PROFILELIST_KEY);
  429. if (key) {
  430. userKey = OpenRegKey (key, EnumPtr->UserName);
  431. if (userKey) {
  432. data = GetRegValueString (userKey, S_PROFILEIMAGEPATH);
  433. if (data) {
  434. FreeMem (data);
  435. } else {
  436. EnumPtr->DefaultUserHive = TRUE;
  437. }
  438. CloseRegKey (userKey);
  439. }
  440. CloseRegKey (key);
  441. }
  442. }
  443. //
  444. // Generate fixed user names
  445. //
  446. if (EnumPtr->EnableNameFix) {
  447. GetUpgradeUserName (EnumPtr->UserName, EnumPtr->FixedUserName);
  448. //
  449. // If this is Administrator, and it is coming from DefaultUser, then
  450. // UserName is empty and we must use the name Administrator for the
  451. // account (or Owner on PER skus).
  452. //
  453. if ((EnumPtr->AccountType & ADMINISTRATOR) &&
  454. EnumPtr->FixedUserName[0] == 0
  455. ) {
  456. StringCopy (EnumPtr->FixedUserName, g_AdministratorStr);
  457. MemDbSetValueEx (
  458. MEMDB_CATEGORY_FIXEDUSERNAMES,
  459. EnumPtr->UserName, // empty string
  460. EnumPtr->FixedUserName, // Administrator or Owner
  461. NULL,
  462. 0,
  463. NULL
  464. );
  465. }
  466. if (EnumPtr->AdminUserName[0]) {
  467. GetUpgradeUserName (EnumPtr->AdminUserName, EnumPtr->FixedAdminUserName);
  468. }
  469. } else {
  470. StringCopy (EnumPtr->FixedUserName, EnumPtr->UserName);
  471. StringCopy (EnumPtr->FixedAdminUserName, EnumPtr->AdminUserName);
  472. }
  473. //
  474. // Map in the hive
  475. //
  476. if (!EnumPtr->DoNotMapHive) {
  477. if (EnumPtr->DefaultUserHive) {
  478. // The default hive
  479. rc = Win95RegSetCurrentUser (
  480. NULL, // User Pos -- NULL for default
  481. NULL, // (IN OPTIONAL) Substitute %WinDir%
  482. EnumPtr->UserDatPath // OUT
  483. );
  484. } else {
  485. // A non-default hive
  486. rc = Win95RegSetCurrentUser (
  487. &EnumPtr->pos,
  488. NULL, // (IN OPTIONAL) Substitute %WinDir%
  489. EnumPtr->UserDatPath
  490. );
  491. }
  492. } else {
  493. if (!EnumPtr->pos.UseProfile || EnumPtr->DefaultUserHive) {
  494. StringCopy (EnumPtr->UserDatPath, g_WinDir);
  495. StringCat (EnumPtr->UserDatPath, TEXT("\\user.dat"));
  496. rc = ERROR_SUCCESS;
  497. } else {
  498. //
  499. // Call FindAndLoadHive to get the user.dat path,
  500. // but don't actually load the hive.
  501. //
  502. rc = FindAndLoadHive (
  503. &EnumPtr->pos,
  504. NULL, // CallerSuppliedWinDir
  505. NULL, // UserDatFromCaller
  506. EnumPtr->UserDatPath,
  507. FALSE // MapTheHive flag
  508. );
  509. }
  510. }
  511. //
  512. // Resolve profile directory
  513. //
  514. if (rc != ERROR_SUCCESS) {
  515. EnumPtr->AccountType |= INVALID_ACCOUNT;
  516. DEBUGMSG ((
  517. DBG_WARNING,
  518. "pUpdateEnumStruct: Win95RegSetCurrentUser could not set user %s (rc=%u)",
  519. EnumPtr->UserName,
  520. rc
  521. ));
  522. } else {
  523. if (!EnumPtr->DoNotMapHive) {
  524. //
  525. // User's hive is valid, open the registry
  526. //
  527. MYASSERT (g_UserKey && *g_UserKey);
  528. if (!g_UserKey) {
  529. g_UserKey = S_EMPTY;
  530. }
  531. EnumPtr->UserRegKey = OpenRegKeyStr (g_UserKey);
  532. if (!EnumPtr->UserRegKey) {
  533. LOG ((LOG_ERROR, "Cannot open %s", g_UserKey));
  534. EnumPtr->State = EnumPtr->NextState;
  535. }
  536. }
  537. //
  538. // Save original profile directory
  539. //
  540. StringCopy (EnumPtr->OrgProfilePath, EnumPtr->UserDatPath);
  541. p = _tcsrchr (EnumPtr->OrgProfilePath, TEXT('\\'));
  542. if (p) {
  543. *p = 0;
  544. }
  545. //
  546. // now build profile directory and path
  547. //
  548. if (EnumPtr->AccountType & ADMINISTRATOR) {
  549. //
  550. // Special case: We know the NT Profile directory name for Administrator.
  551. // It can't come from Win9x.
  552. //
  553. StringCopy (EnumPtr->ProfileDirName, g_AdministratorStr);
  554. } else {
  555. //
  556. // General case: The profile directory is in the user.dat path
  557. //
  558. if (!StringMatch (EnumPtr->UserName, EnumPtr->FixedUserName)) {
  559. //
  560. // Use fixed user name if one exists
  561. //
  562. StringCopy (EnumPtr->ProfileDirName, EnumPtr->FixedUserName);
  563. } else if (StringIMatchCharCount (EnumPtr->UserDatPath, g_ProfileDirWack, g_ProfileDirWackChars)) {
  564. //
  565. // If per-user profile directory exists, extract the user name from it
  566. //
  567. _tcssafecpy (
  568. EnumPtr->ProfileDirName,
  569. CharCountToPointer (EnumPtr->UserDatPath, g_ProfileDirWackChars),
  570. MAX_TCHAR_PATH
  571. );
  572. p = _tcsrchr (EnumPtr->ProfileDirName, TEXT('\\'));
  573. if (p) {
  574. *p = 0;
  575. //
  576. // Unusual case: The directory name we extracted collides with
  577. // another user, Default User, All Users or Administrator.
  578. //
  579. StringCopy (TempDir, EnumPtr->ProfileDirName);
  580. TempDirSeq = 1;
  581. p = _tcschr (TempDir, TEXT('.'));
  582. if (p) {
  583. *p = 0;
  584. }
  585. while (pIsProfileDirInUse (
  586. EnumPtr->ProfileDirTable,
  587. EnumPtr->ProfileDirName,
  588. EnumPtr->UserName
  589. )) {
  590. wsprintf (EnumPtr->ProfileDirName, TEXT("%s.%03u"), TempDir, TempDirSeq);
  591. TempDirSeq++;
  592. if (TempDirSeq == 1000) {
  593. break;
  594. }
  595. }
  596. } else {
  597. //
  598. // Unusual case: No sub dir after profile directory -- copy user name
  599. //
  600. _tcssafecpy (EnumPtr->ProfileDirName, EnumPtr->UserName, MAX_TCHAR_PATH);
  601. }
  602. //
  603. // Add to table for collision detection
  604. //
  605. pSetupStringTableAddString (
  606. EnumPtr->ProfileDirTable,
  607. EnumPtr->ProfileDirName,
  608. STRTAB_CASE_INSENSITIVE
  609. );
  610. } else {
  611. //
  612. // No per-user profile directory -- copy user name
  613. //
  614. _tcssafecpy (EnumPtr->ProfileDirName, EnumPtr->UserName, MAX_TCHAR_PATH);
  615. }
  616. //
  617. // If profile directory is empty, change to All Users
  618. //
  619. if (!EnumPtr->ProfileDirName[0]) {
  620. StringCopy (EnumPtr->ProfileDirName, S_ALL_USERS);
  621. }
  622. }
  623. //
  624. // Generate full path to new profile dir
  625. //
  626. if (*EnumPtr->FixedUserName) {
  627. wsprintf (
  628. EnumPtr->NewProfilePath,
  629. TEXT(">%s"),
  630. EnumPtr->FixedUserName
  631. );
  632. } else {
  633. wsprintf (
  634. EnumPtr->NewProfilePath,
  635. TEXT(">%s"),
  636. EnumPtr->ProfileDirName
  637. );
  638. }
  639. }
  640. //
  641. // Set flag for last logged on user and current user
  642. //
  643. if (StringIMatch (EnumPtr->UserName, EnumPtr->LastLoggedOnUserName)) {
  644. EnumPtr->AccountType |= LAST_LOGGED_ON_USER;
  645. }
  646. if (StringIMatch (EnumPtr->UserName, EnumPtr->CurrentUserName)) {
  647. EnumPtr->AccountType |= CURRENT_USER;
  648. }
  649. }
  650. BOOL
  651. pUserEnumWorker (
  652. IN OUT PUSERENUM EnumPtr
  653. )
  654. /*++
  655. Routine Description:
  656. pUserEnumWorker implements a state machine that enumerates:
  657. 1. All named users
  658. 2. If no named users, the last logged on user (if one exists)
  659. 3. The Administrator account (if not already enumerated in step 1 or 2)
  660. 4. The logon prompt account
  661. 5. The default user (if enabled)
  662. The caller can filter out the create-only Administrator account
  663. and the logon prompt account.
  664. Arguments:
  665. EnumPtr - Specifies the previous enumeration state (or an initialized
  666. enumeration struct). Recieves the next enumerated user.
  667. Return Value:
  668. TRUE if another user was enumerated, or FALSE if no additional users are
  669. left.
  670. --*/
  671. {
  672. DWORD rc;
  673. HKEY Key;
  674. PCTSTR Data;
  675. DWORD Size;
  676. while (EnumPtr->State != UE_STATE_END) {
  677. switch (EnumPtr->State) {
  678. case UE_STATE_INIT:
  679. //
  680. // Init table for collisions...
  681. //
  682. EnumPtr->ProfileDirTable = pSetupStringTableInitialize();
  683. if (!EnumPtr->ProfileDirTable) {
  684. return FALSE;
  685. }
  686. //
  687. // Get data static to the enumeration:
  688. // - Last logged on user
  689. // - Current user
  690. //
  691. Key = OpenRegKeyStr (TEXT("HKLM\\Network\\Logon"));
  692. if (Key) {
  693. Data = GetRegValueString (Key, TEXT("username"));
  694. if (Data) {
  695. _tcssafecpy (EnumPtr->LastLoggedOnUserName, Data, MAX_USER_NAME);
  696. MemFree (g_hHeap, 0, Data);
  697. }
  698. CloseRegKey (Key);
  699. }
  700. Size = MAX_USER_NAME;
  701. if (!GetUserName (EnumPtr->CurrentUserName, &Size)) {
  702. EnumPtr->CurrentUserName[0] = 0;
  703. }
  704. //
  705. // Check for an account named Administrator
  706. //
  707. rc = Win95RegGetFirstUser (&EnumPtr->pos, EnumPtr->UserName);
  708. if (rc != ERROR_SUCCESS) {
  709. EnumPtr->State = UE_STATE_CLEANUP;
  710. LOG ((LOG_ERROR, "Could not enumerate first user. Error: %u.", rc));
  711. break;
  712. }
  713. while (Win95RegHaveUser (&EnumPtr->pos)) {
  714. //
  715. // Add user name to profile dir table
  716. //
  717. pSetupStringTableAddString (
  718. EnumPtr->ProfileDirTable,
  719. EnumPtr->UserName,
  720. STRTAB_CASE_INSENSITIVE
  721. );
  722. //
  723. // If this is Administrator, set flag
  724. //
  725. if (pIsAdministratorUserName (EnumPtr->UserName)) {
  726. EnumPtr->RealAdminAccountExists = TRUE;
  727. }
  728. Win95RegGetNextUser (&EnumPtr->pos, EnumPtr->UserName);
  729. }
  730. EnumPtr->State = UE_STATE_BEGIN_WIN95REG;
  731. break;
  732. case UE_STATE_BEGIN_WIN95REG:
  733. pPrepareStructForNextUser (EnumPtr);
  734. Win95RegGetFirstUser (&EnumPtr->pos, EnumPtr->UserName);
  735. EnumPtr->CommonProfilesEnabled = !EnumPtr->pos.UseProfile;
  736. DEBUGMSG_IF ((EnumPtr->CommonProfilesEnabled, DBG_USERENUM, "Common profiles enabled"));
  737. DEBUGMSG_IF ((!EnumPtr->CommonProfilesEnabled, DBG_USERENUM, "Common profiles disabled"));
  738. EnumPtr->DefaultUserHive = EnumPtr->CommonProfilesEnabled;
  739. if (Win95RegHaveUser (&EnumPtr->pos)) {
  740. //
  741. // We have a user.
  742. //
  743. pPrepareStructForReturn (EnumPtr, NAMED_USER, UE_STATE_NEXT_WIN95REG);
  744. } else {
  745. //
  746. // We have NO users.
  747. //
  748. EnumPtr->State = UE_STATE_NO_USERS;
  749. }
  750. break;
  751. case UE_STATE_NO_USERS:
  752. //
  753. // There are two cases, either there is no logon prompt, or the
  754. // user hit escape and decided to upgrade.
  755. //
  756. pPrepareStructForNextUser (EnumPtr);
  757. //
  758. // No users means no hives.
  759. //
  760. EnumPtr->DefaultUserHive = TRUE;
  761. if (EnumPtr->LastLoggedOnUserName[0]) {
  762. DEBUGMSG ((DBG_USERENUM, "User is not logged on now, but was logged on before."));
  763. StringCopy (EnumPtr->UserName, EnumPtr->LastLoggedOnUserName);
  764. if (pIsAdministratorUserName (EnumPtr->UserName)) {
  765. pPrepareStructForReturn (EnumPtr, NAMED_USER, UE_STATE_LOGON_PROMPT);
  766. } else {
  767. pPrepareStructForReturn (EnumPtr, NAMED_USER, UE_STATE_ADMINISTRATOR);
  768. }
  769. } else {
  770. DEBUGMSG ((DBG_USERENUM, "Machine only has a default user."));
  771. EnumPtr->UserName[0] = 0;
  772. pPrepareStructForReturn (EnumPtr, DEFAULT_USER|ADMINISTRATOR|LOGON_PROMPT|CURRENT_USER, UE_STATE_LOGON_PROMPT);
  773. }
  774. break;
  775. case UE_STATE_NEXT_WIN95REG:
  776. pPrepareStructForNextUser (EnumPtr);
  777. rc = Win95RegGetNextUser (&EnumPtr->pos, EnumPtr->UserName);
  778. if (rc != ERROR_SUCCESS) {
  779. EnumPtr->State = UE_STATE_CLEANUP;
  780. LOG ((LOG_ERROR, "Could not enumerate next user. Error: %u.", rc));
  781. break;
  782. }
  783. if (Win95RegHaveUser (&EnumPtr->pos)) {
  784. //
  785. // We have another user
  786. //
  787. pPrepareStructForReturn (EnumPtr, NAMED_USER, UE_STATE_NEXT_WIN95REG);
  788. } else {
  789. EnumPtr->State = UE_STATE_ADMINISTRATOR;
  790. }
  791. break;
  792. case UE_STATE_ADMINISTRATOR:
  793. //
  794. // Until now, there has been no user named Administrator.
  795. // Enumerate this account only if the caller wants it.
  796. //
  797. if (EnumPtr->WantCreateOnly) {
  798. pPrepareStructForNextUser (EnumPtr);
  799. //
  800. // Enumerate Win95Reg until Administrator is found
  801. //
  802. Win95RegGetFirstUser (&EnumPtr->pos, EnumPtr->UserName);
  803. while (Win95RegHaveUser (&EnumPtr->pos)) {
  804. if (pIsAdministratorUserName (EnumPtr->UserName)) {
  805. break;
  806. }
  807. Win95RegGetNextUser (&EnumPtr->pos, EnumPtr->UserName);
  808. }
  809. if (Win95RegHaveUser (&EnumPtr->pos)) {
  810. //
  811. // If an account named Administrator exists, then
  812. // don't enumerate it again.
  813. //
  814. EnumPtr->State = UE_STATE_LOGON_PROMPT;
  815. break;
  816. }
  817. //
  818. // We used to set all data from the current user. We don't do that any more.
  819. // Administrator data is pretty much similar with default user.
  820. //
  821. EnumPtr->DefaultUserHive = TRUE;
  822. StringCopy (EnumPtr->UserName, g_AdministratorStr);
  823. StringCopy (EnumPtr->AdminUserName, g_AdministratorStr);
  824. EnumPtr->CreateAccountOnly = TRUE;
  825. //
  826. // Now return the user, or default user if the current user is not
  827. // named.
  828. //
  829. pPrepareStructForReturn (EnumPtr, ADMINISTRATOR, UE_STATE_LOGON_PROMPT);
  830. } else {
  831. EnumPtr->State = UE_STATE_LOGON_PROMPT;
  832. }
  833. break;
  834. case UE_STATE_LOGON_PROMPT:
  835. if (EnumPtr->WantLogonPrompt) {
  836. pPrepareStructForNextUser (EnumPtr);
  837. EnumPtr->DefaultUserHive = TRUE;
  838. StringCopy (EnumPtr->UserName, S_DOT_DEFAULT);
  839. pPrepareStructForReturn (EnumPtr, LOGON_PROMPT, UE_STATE_DEFAULT_USER);
  840. } else {
  841. EnumPtr->State = UE_STATE_DEFAULT_USER;
  842. }
  843. break;
  844. case UE_STATE_DEFAULT_USER:
  845. if (g_ConfigOptions.MigrateDefaultUser) {
  846. pPrepareStructForNextUser (EnumPtr);
  847. EnumPtr->DefaultUserHive = TRUE;
  848. StringCopy (EnumPtr->UserName, S_DEFAULT_USER);
  849. pPrepareStructForReturn (EnumPtr, DEFAULT_USER, UE_STATE_CLEANUP);
  850. } else {
  851. EnumPtr->State = UE_STATE_CLEANUP;
  852. }
  853. break;
  854. case UE_STATE_RETURN:
  855. EnumPtr->State = EnumPtr->NextState;
  856. //
  857. // check if certain conditions are met that would prevent
  858. // migration of certain user accounts (like ones that share some shell folders)
  859. //
  860. if (pUserMigrationDisabled (EnumPtr)) {
  861. EnumPtr->AccountType |= INVALID_ACCOUNT;
  862. }
  863. return TRUE;
  864. case UE_STATE_CLEANUP:
  865. if (EnumPtr->UserRegKey) {
  866. CloseRegKey (EnumPtr->UserRegKey);
  867. }
  868. if (EnumPtr->ProfileDirTable) {
  869. pSetupStringTableDestroy (EnumPtr->ProfileDirTable);
  870. }
  871. ZeroMemory (EnumPtr, sizeof (USERENUM));
  872. EnumPtr->State = UE_STATE_END;
  873. break;
  874. }
  875. }
  876. return FALSE;
  877. }
  878. VOID
  879. pFixBrokenNetLogonRegistry (
  880. VOID
  881. )
  882. {
  883. HKEY key;
  884. PCTSTR data;
  885. TCHAR userName[256];
  886. DWORD size;
  887. key = OpenRegKeyStr (TEXT("HKLM\\Network\\Logon"));
  888. if (key) {
  889. data = GetRegValueString (key, TEXT("UserName"));
  890. if (!data) {
  891. size = ARRAYSIZE(userName);
  892. if (GetUserName (userName, &size) && (size > 0)) {
  893. LOG ((
  894. LOG_WARNING,
  895. "HKLM\\Network\\Logon [UserName] is missing; filling it in with %s",
  896. userName
  897. ));
  898. RegSetValueEx (
  899. key,
  900. TEXT("UserName"),
  901. 0,
  902. REG_SZ,
  903. (PBYTE) (userName),
  904. SizeOfString (userName)
  905. );
  906. }
  907. } else {
  908. FreeMem (data);
  909. }
  910. CloseRegKey (key);
  911. }
  912. }
  913. VOID
  914. pRecordUserDoingTheUpgrade (
  915. VOID
  916. )
  917. {
  918. TCHAR userName[256];
  919. DWORD size;
  920. userName[0] = 0;
  921. size = ARRAYSIZE(userName);
  922. GetUserName (userName, &size);
  923. if (userName[0] == 0) {
  924. StringCopy (userName, g_AdministratorStr);
  925. }
  926. MemDbSetValueEx (
  927. MEMDB_CATEGORY_ADMINISTRATOR_INFO,
  928. MEMDB_ITEM_AI_USER_DOING_MIG,
  929. NULL, // no field
  930. userName,
  931. 0,
  932. NULL
  933. );
  934. }
  935. BOOL
  936. EnumFirstUser (
  937. OUT PUSERENUM EnumPtr,
  938. IN DWORD Flags
  939. )
  940. /*++
  941. Routine Description:
  942. EnumFirstUser begins the enumeration of all users to be migrated. This
  943. includes all named users (even ones with broken registries), the
  944. Administrator account, the logon prompt account, and the Default User
  945. account.
  946. Arguments:
  947. EnumPtr - Receives the enumerated user attributes
  948. Flags - Specifies any of the following flags:
  949. ENUMUSER_ENABLE_NAME_FIX - Caller wants the fixed versions of
  950. the user names
  951. ENUMUSER_DO_NOT_MAP_HIVE - Caller wants fast enumeration (no
  952. registry hive map)
  953. ENUMUSER_ADMINISTRATOR_ALWAYS - Caller wants the Administrator
  954. account, even if a user is
  955. not named Administrator
  956. ENUMUSER_INCLUDE_LOGON_PROMPT - Caller wants the logon prompt
  957. account
  958. Return Value:
  959. TRUE if a user was enumerated, or FALSE if not.
  960. --*/
  961. {
  962. //
  963. // first initialize the enumeration engine
  964. //
  965. if (!(g_UserEnumFlags & UE_INITIALIZED)) {
  966. g_UserEnumFlags |= UE_INITIALIZED;
  967. pFixBrokenNetLogonRegistry ();
  968. pRecordUserDoingTheUpgrade ();
  969. pCheckShellFoldersCollision ();
  970. }
  971. //
  972. // Init enum struct
  973. //
  974. ZeroMemory (EnumPtr, sizeof (USERENUM));
  975. //
  976. // Separate the flags
  977. //
  978. EnumPtr->EnableNameFix = (Flags & ENUMUSER_ENABLE_NAME_FIX) != 0;
  979. EnumPtr->DoNotMapHive = (Flags & ENUMUSER_DO_NOT_MAP_HIVE) != 0;
  980. EnumPtr->WantCreateOnly = (Flags & ENUMUSER_ADMINISTRATOR_ALWAYS) != 0;
  981. EnumPtr->WantLogonPrompt = (Flags & ENUMUSER_NO_LOGON_PROMPT) == 0;
  982. //
  983. // Init the state machine
  984. //
  985. EnumPtr->State = UE_STATE_INIT;
  986. //
  987. // Enum the next item
  988. //
  989. return pUserEnumWorker (EnumPtr);
  990. }
  991. BOOL
  992. EnumNextUser (
  993. IN OUT PUSERENUM EnumPtr
  994. )
  995. {
  996. return pUserEnumWorker (EnumPtr);
  997. }
  998. VOID
  999. EnumUserAbort (
  1000. IN OUT PUSERENUM EnumPtr
  1001. )
  1002. {
  1003. if (EnumPtr->State != UE_STATE_END &&
  1004. EnumPtr->State != UE_STATE_INIT
  1005. ) {
  1006. EnumPtr->State = UE_STATE_CLEANUP;
  1007. pUserEnumWorker (EnumPtr);
  1008. }
  1009. }