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.

995 lines
39 KiB

  1. // --------------------------------------------------------------------------
  2. // Module Name: UserList.cpp
  3. //
  4. // Copyright (c) 1999-2000, Microsoft Corporation
  5. //
  6. // Class that implements the user list filtering algorithm shared by winlogon
  7. // calling into msgina and shgina (the logonocx) calling into msgina.
  8. //
  9. // History: 1999-10-30 vtan created
  10. // 1999-11-26 vtan moved from logonocx
  11. // 2000-01-31 vtan moved from Neptune to Whistler
  12. // --------------------------------------------------------------------------
  13. #include "StandardHeader.h"
  14. #include "UserList.h"
  15. #include <shlwapi.h>
  16. #include <shlwapip.h>
  17. #include <winsta.h>
  18. #include "RegistryResources.h"
  19. #include "SpecialAccounts.h"
  20. #include "SystemSettings.h"
  21. // --------------------------------------------------------------------------
  22. // CUserList::s_SIDAdministrator
  23. // CUserList::s_SIDGuest
  24. // CUserList::s_szAdministratorsGroupName
  25. // CUserList::s_szPowerUsersGroupName
  26. // CUserList::s_szUsersGroupName
  27. // CUserList::s_szGuestsGroupName
  28. //
  29. // Purpose: Stores the localized name of the well known accounts
  30. // "Administrator" and "Guest". These accounts are determined
  31. // by SID. Also stores the localized name of the local
  32. // "Administrators" group.
  33. //
  34. // History: 2000-02-15 vtan created
  35. // 2000-03-06 vtan added Administrators group
  36. // 2001-05-10 vtan changed user strings to SID
  37. // --------------------------------------------------------------------------
  38. unsigned char CUserList::s_SIDAdministrator[256] = { 0 };
  39. unsigned char CUserList::s_SIDGuest[256] = { 0 };
  40. WCHAR CUserList::s_szAdministratorsGroupName[GNLEN + sizeof('\0')] = { L'\0' };
  41. WCHAR CUserList::s_szPowerUsersGroupName[GNLEN + sizeof('\0')] = { L'\0' };
  42. WCHAR CUserList::s_szUsersGroupName[GNLEN + sizeof('\0')] = { L'\0' };
  43. WCHAR CUserList::s_szGuestsGroupName[GNLEN + sizeof('\0')] = { L'\0' };
  44. // --------------------------------------------------------------------------
  45. // CUserList::Get
  46. //
  47. // Arguments: fRemoveGuest = Always remove the "Guest" account.
  48. // pdwReturnEntryCount = Returned number of entries. This
  49. // may be NULL.
  50. // pUserList = Buffer containing user data. This
  51. // may be NULL.
  52. //
  53. // Returns: LONG
  54. //
  55. // Purpose: Returns a filtered array of user entries from the given
  56. // server SAM. Filtering is performed here so that a common
  57. // algorithm can be applied to the list of users such that the
  58. // logon UI host can display the correct user information and
  59. // msgina can return the same number of users on the system.
  60. //
  61. // History: 1999-10-15 vtan created
  62. // 1999-10-30 vtan uses CSpecialAccounts
  63. // 1999-11-26 vtan moved from logonocx
  64. // --------------------------------------------------------------------------
  65. LONG CUserList::Get (bool fRemoveGuest, DWORD *pdwReturnedEntryCount, GINA_USER_INFORMATION* *pReturnedUserList)
  66. {
  67. LONG lError;
  68. DWORD dwPreferredSize, dwEntryCount, dwEntriesRead;
  69. GINA_USER_INFORMATION *pUserList;
  70. NET_DISPLAY_USER *pNDU;
  71. CSpecialAccounts SpecialAccounts;
  72. pUserList = NULL;
  73. dwEntryCount = 0;
  74. // Determine the well known account names.
  75. DetermineWellKnownAccountNames();
  76. // Allow a buffer for 100 users including their name, comments and full name.
  77. // This should be sufficient for home consumers. If the need to extend this
  78. // arises make this dynamic!
  79. dwPreferredSize = (sizeof(NET_DISPLAY_USER) + (3 * UNLEN) * s_iMaximumUserCount);
  80. pNDU = NULL;
  81. lError = NetQueryDisplayInformation(NULL, // NULL means LocalMachine
  82. 1, // query User information
  83. 0, // starting with the first user
  84. s_iMaximumUserCount, // return a max of 100 users
  85. dwPreferredSize, // preferred buffer size
  86. &dwEntriesRead,
  87. reinterpret_cast<void**>(&pNDU));
  88. if ((ERROR_SUCCESS == lError) || (ERROR_MORE_DATA == lError))
  89. {
  90. bool fHasCreatedAccount, fFound;
  91. DWORD dwUsernameSize;
  92. int iIndex, iAdministratorIndex;
  93. WCHAR wszUsername[UNLEN + sizeof('\0')];
  94. // Get the current user name.
  95. dwUsernameSize = ARRAYSIZE(wszUsername);
  96. if (GetUserNameW(wszUsername, &dwUsernameSize) == FALSE)
  97. {
  98. wszUsername[0] = L'\0';
  99. }
  100. fHasCreatedAccount = false;
  101. iAdministratorIndex = -1;
  102. for (iIndex = static_cast<int>(dwEntriesRead - 1); iIndex >= 0; --iIndex)
  103. {
  104. PSID pSID;
  105. pSID = ConvertNameToSID(pNDU[iIndex].usri1_name);
  106. if (pSID != NULL)
  107. {
  108. // Never filter the current user.
  109. if (lstrcmpiW(pNDU[iIndex].usri1_name, wszUsername) == 0)
  110. {
  111. // If this is executed in the current user context and
  112. // that user isn't "Administrator", but is a member of
  113. // the local administrators group, then a user created
  114. // administrator account exists even though it isn't
  115. // filtered. The "Administrator" account can be removed.
  116. if ((EqualSid(pSID, s_SIDAdministrator) == FALSE) &&
  117. IsUserMemberOfLocalAdministrators(pNDU[iIndex].usri1_name))
  118. {
  119. fHasCreatedAccount = true;
  120. if (iAdministratorIndex >= 0)
  121. {
  122. DeleteEnumerateUsers(pNDU, dwEntriesRead, iAdministratorIndex);
  123. iAdministratorIndex = -1;
  124. }
  125. }
  126. }
  127. else
  128. {
  129. // If the account is
  130. // 1) disabled
  131. // 2) locked out
  132. // 3) a special account (see CSpecialAccounts)
  133. // 4) "Guest" and fRemoveGuest is true
  134. // 5) "Administrator" and has created another account
  135. // and does not always include "Administrator" and
  136. // "Administrator is not logged on
  137. // Then filter the account out.
  138. if (((pNDU[iIndex].usri1_flags & UF_ACCOUNTDISABLE) != 0) ||
  139. ((pNDU[iIndex].usri1_flags & UF_LOCKOUT) != 0) ||
  140. SpecialAccounts.AlwaysExclude(pNDU[iIndex].usri1_name) ||
  141. (fRemoveGuest && (EqualSid(pSID, s_SIDGuest) != FALSE)) ||
  142. ((EqualSid(pSID, s_SIDAdministrator) != FALSE) &&
  143. fHasCreatedAccount &&
  144. !SpecialAccounts.AlwaysInclude(pNDU[iIndex].usri1_name) &&
  145. !IsUserLoggedOn(pNDU[iIndex].usri1_name, NULL)))
  146. {
  147. DeleteEnumerateUsers(pNDU, dwEntriesRead, iIndex);
  148. // Account for indices being changed.
  149. // If this index wasn't set previously it just goes more negative.
  150. // If it was set we know it can never be below zero.
  151. --iAdministratorIndex;
  152. }
  153. // If the account should always be included then do it.
  154. // Guest is not a user created account so fHasCreatedAccount
  155. // must not be set if this account is seen.
  156. else if (!SpecialAccounts.AlwaysInclude(pNDU[iIndex].usri1_name))
  157. {
  158. // If safe mode then filter accounts that are not members of the
  159. // local administrators group.
  160. if (CSystemSettings::IsSafeMode())
  161. {
  162. if (!IsUserMemberOfLocalAdministrators(pNDU[iIndex].usri1_name))
  163. {
  164. DeleteEnumerateUsers(pNDU, dwEntriesRead, iIndex);
  165. --iAdministratorIndex;
  166. }
  167. }
  168. else if (EqualSid(pSID, s_SIDAdministrator) != FALSE)
  169. {
  170. if (!IsUserLoggedOn(pNDU[iIndex].usri1_name, NULL))
  171. {
  172. // Otherwise if the account name is "Administrator" and another
  173. // account has been created then this account needs to be removed
  174. // from the list. If another account has not been seen then
  175. // remember this index so that if another account is seen this
  176. // account can be removed.
  177. if (fHasCreatedAccount)
  178. {
  179. DeleteEnumerateUsers(pNDU, dwEntriesRead, iIndex);
  180. --iAdministratorIndex;
  181. }
  182. else
  183. {
  184. iAdministratorIndex = iIndex;
  185. }
  186. }
  187. }
  188. else if (EqualSid(pSID, s_SIDGuest) == FALSE)
  189. {
  190. // If the account name is NOT "Administrator" then check the
  191. // account group membership. If the account is a member of the
  192. // local administrators group then the "Administrator" account
  193. // can be removed.
  194. if (IsUserMemberOfLocalAdministrators(pNDU[iIndex].usri1_name))
  195. {
  196. fHasCreatedAccount = true;
  197. if (iAdministratorIndex >= 0)
  198. {
  199. DeleteEnumerateUsers(pNDU, dwEntriesRead, iAdministratorIndex);
  200. iAdministratorIndex = -1;
  201. }
  202. }
  203. if (!IsUserMemberOfLocalKnownGroup(pNDU[iIndex].usri1_name))
  204. {
  205. DeleteEnumerateUsers(pNDU, dwEntriesRead, iIndex);
  206. --iAdministratorIndex;
  207. }
  208. }
  209. }
  210. }
  211. (HLOCAL)LocalFree(pSID);
  212. }
  213. }
  214. if (!ParseDisplayInformation(pNDU, dwEntriesRead, pUserList, dwEntryCount))
  215. {
  216. lError = ERROR_OUTOFMEMORY;
  217. pUserList = NULL;
  218. dwEntryCount = 0;
  219. }
  220. (NET_API_STATUS)NetApiBufferFree(pNDU);
  221. if (ERROR_SUCCESS == lError)
  222. {
  223. // Sort the user list. Typically this has come back alphabetized by the
  224. // SAM. However, the SAM sorts by logon name and not by display name.
  225. // This needs to be sorted by display name.
  226. Sort(pUserList, dwEntryCount);
  227. // The guest account should be put at the end of this list. This
  228. // is a simple case of find the guest account (by localized name) and
  229. // sliding all the entries down and inserting the guest at the end.
  230. for (fFound = false, iIndex = 0; !fFound && (iIndex < static_cast<int>(dwEntryCount)); ++iIndex)
  231. {
  232. PSID pSID;
  233. pSID = ConvertNameToSID(pUserList[iIndex].pszName);
  234. if (pSID != NULL)
  235. {
  236. fFound = (EqualSid(pSID, s_SIDGuest) != FALSE);
  237. if (fFound)
  238. {
  239. GINA_USER_INFORMATION gui;
  240. MoveMemory(&gui, &pUserList[iIndex], sizeof(gui));
  241. MoveMemory(&pUserList[iIndex], &pUserList[iIndex + 1], (dwEntryCount - iIndex - 1) * sizeof(pUserList[0]));
  242. MoveMemory(&pUserList[dwEntryCount - 1], &gui, sizeof(gui));
  243. }
  244. (HLOCAL)LocalFree(pSID);
  245. }
  246. }
  247. }
  248. }
  249. if (pReturnedUserList != NULL)
  250. {
  251. *pReturnedUserList = pUserList;
  252. }
  253. else
  254. {
  255. ReleaseMemory(pUserList);
  256. }
  257. if (pdwReturnedEntryCount != NULL)
  258. {
  259. *pdwReturnedEntryCount = dwEntryCount;
  260. }
  261. return(lError);
  262. }
  263. // --------------------------------------------------------------------------
  264. // CLogonDialog::IsUserLoggedOn
  265. //
  266. // Arguments: pszUsername = User name.
  267. // pszDomain = User domain.
  268. //
  269. // Returns: bool
  270. //
  271. // Purpose: Use WindowStation APIs in terminal services to determine if
  272. // a given user is logged onto this machine. It will not query
  273. // remote terminal servers.
  274. //
  275. // Windowstations must be in the active or disconnected state.
  276. //
  277. // History: 2000-02-28 vtan created
  278. // 2000-05-30 vtan moved from CWLogonDialog.cpp
  279. // --------------------------------------------------------------------------
  280. bool CUserList::IsUserLoggedOn (const WCHAR *pszUsername, const WCHAR *pszDomain)
  281. {
  282. bool fResult;
  283. WCHAR szDomain[DNLEN + sizeof('\0')];
  284. fResult = false;
  285. // If no domain is supplied then use the computer's name.
  286. if ((pszDomain == NULL) || (pszDomain[0] == L'\0'))
  287. {
  288. DWORD dwDomainSize;
  289. dwDomainSize = ARRAYSIZE(szDomain);
  290. if (GetComputerNameW(szDomain, &dwDomainSize) != FALSE)
  291. {
  292. pszDomain = szDomain;
  293. }
  294. }
  295. // If no domain is supplied and the computer's name cannot be determined
  296. // then this API fails. A user name must also be supplied.
  297. if ((pszUsername != NULL) && (pszDomain != NULL))
  298. {
  299. HANDLE hServer;
  300. PLOGONID pLogonID, pLogonIDs;
  301. ULONG ul, ulEntries;
  302. // Open a connection to terminal services and get the number of sessions.
  303. hServer = WinStationOpenServerW(reinterpret_cast<WCHAR*>(SERVERNAME_CURRENT));
  304. if (hServer != NULL)
  305. {
  306. if (WinStationEnumerate(hServer, &pLogonIDs, &ulEntries) != FALSE)
  307. {
  308. // Iterate the sessions looking for active and disconnected sessions only.
  309. // Then match the user name and domain (case INsensitive) for a result.
  310. for (ul = 0, pLogonID = pLogonIDs; !fResult && (ul < ulEntries); ++ul, ++pLogonID)
  311. {
  312. if ((pLogonID->State == State_Active) || (pLogonID->State == State_Disconnected))
  313. {
  314. ULONG ulReturnLength;
  315. WINSTATIONINFORMATIONW winStationInformation;
  316. if (WinStationQueryInformationW(hServer,
  317. pLogonID->LogonId,
  318. WinStationInformation,
  319. &winStationInformation,
  320. sizeof(winStationInformation),
  321. &ulReturnLength) != FALSE)
  322. {
  323. fResult = ((lstrcmpiW(pszUsername, winStationInformation.UserName) == 0) &&
  324. (lstrcmpiW(pszDomain, winStationInformation.Domain) == 0));
  325. }
  326. }
  327. }
  328. // Free any resources used.
  329. (BOOLEAN)WinStationFreeMemory(pLogonIDs);
  330. }
  331. (BOOLEAN)WinStationCloseServer(hServer);
  332. }
  333. }
  334. return(fResult);
  335. }
  336. // --------------------------------------------------------------------------
  337. // CUserList::IsInteractiveLogonAllowed
  338. //
  339. // Arguments: pszUsername = User name.
  340. //
  341. // Returns: int
  342. //
  343. // Purpose: Determines whether the SeDenyInteractiveLogonRight is
  344. // assigned into the given user. Returns -1 if the state cannot
  345. // be determined due to some error. Otherwise returns 0 if the
  346. // the right is assigned and != 0 && != -1 if not.
  347. //
  348. // One final check is made on personal for a user name that
  349. // matches DOMAIN_USER_RID_ADMIN.
  350. //
  351. // History: 2000-08-15 vtan created
  352. // --------------------------------------------------------------------------
  353. int CUserList::IsInteractiveLogonAllowed (const WCHAR *pszUsername)
  354. {
  355. int iResult;
  356. LSA_HANDLE hLSA;
  357. UNICODE_STRING strDenyInteractiveLogonRight;
  358. OBJECT_ATTRIBUTES objectAttributes;
  359. iResult = -1;
  360. RtlInitUnicodeString(&strDenyInteractiveLogonRight, SE_DENY_INTERACTIVE_LOGON_NAME);
  361. InitializeObjectAttributes(&objectAttributes,
  362. NULL,
  363. 0,
  364. NULL,
  365. NULL);
  366. if (NT_SUCCESS(LsaOpenPolicy(NULL,
  367. &objectAttributes,
  368. POLICY_LOOKUP_NAMES,
  369. &hLSA)))
  370. {
  371. SID_NAME_USE eUse;
  372. DWORD dwSIDSize, dwReferencedDomainSize;
  373. PSID pSID;
  374. WCHAR szReferencedDomain[CNLEN + sizeof('\0')];
  375. dwSIDSize = 0;
  376. dwReferencedDomainSize = ARRAYSIZE(szReferencedDomain);
  377. (BOOL)LookupAccountNameW(NULL,
  378. pszUsername,
  379. NULL,
  380. &dwSIDSize,
  381. szReferencedDomain,
  382. &dwReferencedDomainSize,
  383. &eUse);
  384. pSID = static_cast<PSID>(LocalAlloc(LMEM_FIXED, dwSIDSize));
  385. if (pSID != NULL)
  386. {
  387. if (LookupAccountNameW(NULL,
  388. pszUsername,
  389. pSID,
  390. &dwSIDSize,
  391. szReferencedDomain,
  392. &dwReferencedDomainSize,
  393. &eUse) != FALSE)
  394. {
  395. NTSTATUS status;
  396. ULONG ulIndex, ulCountOfRights;
  397. PLSA_UNICODE_STRING pUserRights;
  398. status = LsaEnumerateAccountRights(hLSA,
  399. pSID,
  400. &pUserRights,
  401. &ulCountOfRights);
  402. if (NT_SUCCESS(status))
  403. {
  404. bool fFound;
  405. for (fFound = false, ulIndex = 0; !fFound && (ulIndex < ulCountOfRights); ++ulIndex)
  406. {
  407. fFound = (RtlEqualUnicodeString(&strDenyInteractiveLogonRight, pUserRights + ulIndex, TRUE) != FALSE);
  408. }
  409. iResult = fFound ? 0 : 1;
  410. TSTATUS(LsaFreeMemory(pUserRights));
  411. }
  412. else if (STATUS_OBJECT_NAME_NOT_FOUND == status)
  413. {
  414. iResult = 1;
  415. }
  416. }
  417. (HLOCAL)LocalFree(pSID);
  418. }
  419. TSTATUS(LsaClose(hLSA));
  420. }
  421. if (IsOS(OS_PERSONAL) && !CSystemSettings::IsSafeMode())
  422. {
  423. PSID pSID;
  424. pSID = ConvertNameToSID(pszUsername);
  425. if (pSID != NULL)
  426. {
  427. if (EqualSid(pSID, s_SIDAdministrator) != FALSE)
  428. {
  429. iResult = 0;
  430. }
  431. (HLOCAL)LocalFree(pSID);
  432. }
  433. }
  434. return(iResult);
  435. }
  436. PSID CUserList::ConvertNameToSID (const WCHAR *pszUsername)
  437. {
  438. PSID pSID;
  439. DWORD dwSIDSize, dwDomainSize;
  440. SID_NAME_USE eUse;
  441. pSID = NULL;
  442. dwSIDSize = dwDomainSize = 0;
  443. (BOOL)LookupAccountNameW(NULL,
  444. pszUsername,
  445. NULL,
  446. &dwSIDSize,
  447. NULL,
  448. &dwDomainSize,
  449. NULL);
  450. if ((dwSIDSize != 0) && (dwDomainSize != 0))
  451. {
  452. WCHAR *pszDomain;
  453. pszDomain = static_cast<WCHAR*>(LocalAlloc(LMEM_FIXED, dwDomainSize * sizeof(WCHAR)));
  454. if (pszDomain != NULL)
  455. {
  456. pSID = static_cast<PSID>(LocalAlloc(LMEM_FIXED, dwSIDSize));
  457. if (pSID != NULL)
  458. {
  459. if (LookupAccountName(NULL,
  460. pszUsername,
  461. pSID,
  462. &dwSIDSize,
  463. pszDomain,
  464. &dwDomainSize,
  465. &eUse) == FALSE)
  466. {
  467. (HLOCAL)LocalFree(pSID);
  468. pSID = NULL;
  469. }
  470. }
  471. (HLOCAL)LocalFree(pszDomain);
  472. }
  473. }
  474. return(pSID);
  475. }
  476. // --------------------------------------------------------------------------
  477. // CUserList::IsUserMemberOfLocalAdministrators
  478. //
  479. // Arguments: pszName = User name to test.
  480. //
  481. // Returns: bool
  482. //
  483. // Purpose: Returns whether the given user is a member of the local
  484. // Administrators group.
  485. //
  486. // History: 2000-03-28 vtan created
  487. // --------------------------------------------------------------------------
  488. bool CUserList::IsUserMemberOfLocalAdministrators (const WCHAR *pszName)
  489. {
  490. bool fIsAnAdministrator;
  491. DWORD dwGroupEntriesRead, dwGroupTotalEntries;
  492. LOCALGROUP_USERS_INFO_0 *pLocalGroupUsersInfo;
  493. fIsAnAdministrator = false;
  494. pLocalGroupUsersInfo = NULL;
  495. if (NetUserGetLocalGroups(NULL,
  496. pszName,
  497. 0,
  498. LG_INCLUDE_INDIRECT,
  499. (LPBYTE*)&pLocalGroupUsersInfo,
  500. MAX_PREFERRED_LENGTH,
  501. &dwGroupEntriesRead,
  502. &dwGroupTotalEntries) == NERR_Success)
  503. {
  504. int iIndexGroup;
  505. LOCALGROUP_USERS_INFO_0 *pLGUI;
  506. for (iIndexGroup = 0, pLGUI = pLocalGroupUsersInfo; !fIsAnAdministrator && (iIndexGroup < static_cast<int>(dwGroupEntriesRead)); ++iIndexGroup, ++pLGUI)
  507. {
  508. fIsAnAdministrator = (lstrcmpiW(pLGUI->lgrui0_name, s_szAdministratorsGroupName) == 0);
  509. }
  510. }
  511. else
  512. {
  513. fIsAnAdministrator = true;
  514. }
  515. if (pLocalGroupUsersInfo != NULL)
  516. {
  517. TW32(NetApiBufferFree(pLocalGroupUsersInfo));
  518. }
  519. return(fIsAnAdministrator);
  520. }
  521. // --------------------------------------------------------------------------
  522. // CUserList::IsUserMemberOfLocalKnownGroup
  523. //
  524. // Arguments: pszName = User name to test.
  525. //
  526. // Returns: bool
  527. //
  528. // Purpose: Returns whether the given user is a member of a local known
  529. // group. Membership of a known group returns true. Membership
  530. // of only groups that are not known returns false.
  531. //
  532. // History: 2000-06-29 vtan created
  533. // --------------------------------------------------------------------------
  534. bool CUserList::IsUserMemberOfLocalKnownGroup (const WCHAR *pszName)
  535. {
  536. bool fIsMember;
  537. DWORD dwGroupEntriesRead, dwGroupTotalEntries;
  538. LOCALGROUP_USERS_INFO_0 *pLocalGroupUsersInfo;
  539. fIsMember = true;
  540. pLocalGroupUsersInfo = NULL;
  541. if (NetUserGetLocalGroups(NULL,
  542. pszName,
  543. 0,
  544. LG_INCLUDE_INDIRECT,
  545. (LPBYTE*)&pLocalGroupUsersInfo,
  546. MAX_PREFERRED_LENGTH,
  547. &dwGroupEntriesRead,
  548. &dwGroupTotalEntries) == NERR_Success)
  549. {
  550. int iIndexGroup;
  551. LOCALGROUP_USERS_INFO_0 *pLGUI;
  552. // Assume the worst. As soon as a known group is found this will terminate the loop.
  553. fIsMember = false;
  554. for (iIndexGroup = 0, pLGUI = pLocalGroupUsersInfo; !fIsMember && (iIndexGroup < static_cast<int>(dwGroupEntriesRead)); ++iIndexGroup, ++pLGUI)
  555. {
  556. fIsMember = ((lstrcmpiW(pLGUI->lgrui0_name, s_szAdministratorsGroupName) == 0) ||
  557. (lstrcmpiW(pLGUI->lgrui0_name, s_szPowerUsersGroupName) == 0) ||
  558. (lstrcmpiW(pLGUI->lgrui0_name, s_szUsersGroupName) == 0) ||
  559. (lstrcmpiW(pLGUI->lgrui0_name, s_szGuestsGroupName) == 0));
  560. }
  561. }
  562. if (pLocalGroupUsersInfo != NULL)
  563. {
  564. TW32(NetApiBufferFree(pLocalGroupUsersInfo));
  565. }
  566. return(fIsMember);
  567. }
  568. // --------------------------------------------------------------------------
  569. // CUserList::DeleteEnumerateUsers
  570. //
  571. // Arguments: pNDU = NET_DISPLAY_USER array to delete from.
  572. // dwEntriesRead = Number of entries in the array.
  573. // iIndex = Index to delete.
  574. //
  575. // Returns: <none>
  576. //
  577. // Purpose: Deletes the given array index contents from the array by
  578. // sliding down the elements and zeroing the last entry.
  579. //
  580. // History: 1999-10-16 vtan created
  581. // 1999-11-26 vtan moved from logonocx
  582. // --------------------------------------------------------------------------
  583. void CUserList::DeleteEnumerateUsers (NET_DISPLAY_USER *pNDU, DWORD& dwEntriesRead, int iIndex)
  584. {
  585. int iIndiciesToMove;
  586. iIndiciesToMove = static_cast<int>(dwEntriesRead - 1) - iIndex;
  587. if (iIndiciesToMove != 0)
  588. {
  589. MoveMemory(&pNDU[iIndex], &pNDU[iIndex + 1], iIndiciesToMove * sizeof(*pNDU));
  590. }
  591. ZeroMemory(&pNDU[--dwEntriesRead], sizeof(*pNDU));
  592. }
  593. // --------------------------------------------------------------------------
  594. // CUserList::DetermineWellKnownAccountNames
  595. //
  596. // Arguments: <none>
  597. //
  598. // Returns: <none>
  599. //
  600. // Purpose: Determines the string for the local Administrator and Guest
  601. // accounts by getting the user list from the local SAM and
  602. // looking up the SID corresponding with the iterated user names
  603. // and checking the SID for the RID that is desired.
  604. //
  605. // The main loop structure mimics the filter function.
  606. //
  607. // History: 2000-02-15 vtan created
  608. // --------------------------------------------------------------------------
  609. void CUserList::DetermineWellKnownAccountNames (void)
  610. {
  611. static bool s_fCachedWellKnownAccountNames = false;
  612. // If the well known account names haven't been determined yet
  613. // then do this. But only do this once.
  614. if (!s_fCachedWellKnownAccountNames)
  615. {
  616. USER_MODALS_INFO_2 *pUMI;
  617. PSID pSID;
  618. DWORD dwNameSize, dwDomainSize;
  619. SID_NAME_USE eUse;
  620. WCHAR szDomain[DNLEN + sizeof('\0')];
  621. // Build the SID for the built-in local administrator
  622. // and built-in local guest accounts.
  623. if (NetUserModalsGet(NULL, 2, (LPBYTE*)&pUMI) == NERR_Success)
  624. {
  625. unsigned char ucSubAuthorityCount;
  626. ucSubAuthorityCount = *GetSidSubAuthorityCount(pUMI->usrmod2_domain_id);
  627. if (GetSidLengthRequired(ucSubAuthorityCount + 1) <= sizeof(s_SIDAdministrator))
  628. {
  629. if (CopySid(GetSidLengthRequired(ucSubAuthorityCount + 1), s_SIDAdministrator, pUMI->usrmod2_domain_id) != FALSE)
  630. {
  631. *GetSidSubAuthority(s_SIDAdministrator, ucSubAuthorityCount) = DOMAIN_USER_RID_ADMIN;
  632. *GetSidSubAuthorityCount(s_SIDAdministrator) = ucSubAuthorityCount + 1;
  633. }
  634. }
  635. else
  636. {
  637. ZeroMemory(s_SIDAdministrator, sizeof(s_SIDAdministrator));
  638. }
  639. if (GetSidLengthRequired(ucSubAuthorityCount + 1) <= sizeof(s_SIDGuest))
  640. {
  641. if (CopySid(GetSidLengthRequired(ucSubAuthorityCount + 1), s_SIDGuest, pUMI->usrmod2_domain_id) != FALSE)
  642. {
  643. *GetSidSubAuthority(s_SIDGuest, ucSubAuthorityCount) = DOMAIN_USER_RID_GUEST;
  644. *GetSidSubAuthorityCount(s_SIDGuest) = ucSubAuthorityCount + 1;
  645. }
  646. }
  647. else
  648. {
  649. ZeroMemory(s_SIDAdministrator, sizeof(s_SIDAdministrator));
  650. }
  651. (NET_API_STATUS)NetApiBufferFree(pUMI);
  652. }
  653. // Now determine the local administrators group name.
  654. static SID_IDENTIFIER_AUTHORITY sSystemSidAuthority = SECURITY_NT_AUTHORITY;
  655. if (NT_SUCCESS(RtlAllocateAndInitializeSid(&sSystemSidAuthority,
  656. 2,
  657. SECURITY_BUILTIN_DOMAIN_RID,
  658. DOMAIN_ALIAS_RID_ADMINS,
  659. 0, 0, 0, 0, 0, 0,
  660. &pSID)))
  661. {
  662. dwNameSize = ARRAYSIZE(s_szAdministratorsGroupName);
  663. dwDomainSize = ARRAYSIZE(szDomain);
  664. TBOOL(LookupAccountSidW(NULL,
  665. pSID,
  666. s_szAdministratorsGroupName,
  667. &dwNameSize,
  668. szDomain,
  669. &dwDomainSize,
  670. &eUse));
  671. (void*)RtlFreeSid(pSID);
  672. }
  673. // Power Users
  674. if (NT_SUCCESS(RtlAllocateAndInitializeSid(&sSystemSidAuthority,
  675. 2,
  676. SECURITY_BUILTIN_DOMAIN_RID,
  677. DOMAIN_ALIAS_RID_POWER_USERS,
  678. 0, 0, 0, 0, 0, 0,
  679. &pSID)))
  680. {
  681. dwNameSize = ARRAYSIZE(s_szPowerUsersGroupName);
  682. dwDomainSize = ARRAYSIZE(szDomain);
  683. (BOOL)LookupAccountSidW(NULL,
  684. pSID,
  685. s_szPowerUsersGroupName,
  686. &dwNameSize,
  687. szDomain,
  688. &dwDomainSize,
  689. &eUse);
  690. (void*)RtlFreeSid(pSID);
  691. }
  692. // Users
  693. if (NT_SUCCESS(RtlAllocateAndInitializeSid(&sSystemSidAuthority,
  694. 2,
  695. SECURITY_BUILTIN_DOMAIN_RID,
  696. DOMAIN_ALIAS_RID_USERS,
  697. 0, 0, 0, 0, 0, 0,
  698. &pSID)))
  699. {
  700. dwNameSize = ARRAYSIZE(s_szUsersGroupName);
  701. dwDomainSize = ARRAYSIZE(szDomain);
  702. TBOOL(LookupAccountSidW(NULL,
  703. pSID,
  704. s_szUsersGroupName,
  705. &dwNameSize,
  706. szDomain,
  707. &dwDomainSize,
  708. &eUse));
  709. (void*)RtlFreeSid(pSID);
  710. }
  711. // Guests
  712. if (NT_SUCCESS(RtlAllocateAndInitializeSid(&sSystemSidAuthority,
  713. 2,
  714. SECURITY_BUILTIN_DOMAIN_RID,
  715. DOMAIN_ALIAS_RID_GUESTS,
  716. 0, 0, 0, 0, 0, 0,
  717. &pSID)))
  718. {
  719. dwNameSize = ARRAYSIZE(s_szGuestsGroupName);
  720. dwDomainSize = ARRAYSIZE(szDomain);
  721. TBOOL(LookupAccountSidW(NULL,
  722. pSID,
  723. s_szGuestsGroupName,
  724. &dwNameSize,
  725. szDomain,
  726. &dwDomainSize,
  727. &eUse));
  728. (void*)RtlFreeSid(pSID);
  729. }
  730. // Don't do this again.
  731. s_fCachedWellKnownAccountNames = true;
  732. }
  733. }
  734. // --------------------------------------------------------------------------
  735. // CUserList::ParseDisplayInformation
  736. //
  737. // Arguments: pNDU = NET_DISPLAY_USER list to parse.
  738. // dwEntriesRead = Number of entries in NDU list.
  739. // pUserList = GINA_USER_INFORMATION pointer returned.
  740. // dwEntryCount = Number of entries in GUI list.
  741. //
  742. // Returns: bool
  743. //
  744. // Purpose: Converts NET_DISPLAY_USER array to GINA_USER_INFORMATION
  745. // array so that information can be added or removed as desired
  746. // from the final information returned to the caller.
  747. //
  748. // History: 2000-06-26 vtan created
  749. // --------------------------------------------------------------------------
  750. bool CUserList::ParseDisplayInformation (NET_DISPLAY_USER *pNDU, DWORD dwEntriesRead, GINA_USER_INFORMATION*& pUserList, DWORD& dwEntryCount)
  751. {
  752. bool fResult;
  753. DWORD dwBufferSize, dwComputerNameSize;
  754. int iIndex;
  755. unsigned char *pBuffer;
  756. WCHAR *pWC;
  757. WCHAR szComputerName[CNLEN + sizeof('\0')];
  758. // Get the local computer name. This is the local domain.
  759. dwComputerNameSize = ARRAYSIZE(szComputerName);
  760. if (GetComputerNameW(szComputerName, &dwComputerNameSize) == FALSE)
  761. {
  762. szComputerName[0] = L'\0';
  763. }
  764. // Calculate the total size of the buffer required based on the number of
  765. // entries and the size of a struct and the length of the strings required.
  766. // Append any additions to the below this loop.
  767. dwBufferSize = 0;
  768. for (iIndex = static_cast<int>(dwEntriesRead - 1); iIndex >= 0; --iIndex)
  769. {
  770. dwBufferSize += sizeof(GINA_USER_INFORMATION);
  771. dwBufferSize += (lstrlenW(pNDU[iIndex].usri1_name) + sizeof('\0')) * sizeof(WCHAR);
  772. dwBufferSize += (lstrlenW(szComputerName) + sizeof('\0')) * sizeof(WCHAR);
  773. dwBufferSize += (lstrlenW(pNDU[iIndex].usri1_full_name) + sizeof('\0')) * sizeof(WCHAR);
  774. }
  775. // Allocate the buffer. Start allocating structs from the start of the
  776. // buffer and allocate strings from the end of the buffer. Uses pUserList
  777. // to allocate structs and pWC to allocate strings.
  778. pBuffer = static_cast<unsigned char*>(LocalAlloc(LMEM_FIXED, dwBufferSize));
  779. pUserList = reinterpret_cast<GINA_USER_INFORMATION*>(pBuffer);
  780. pWC = reinterpret_cast<WCHAR*>(pBuffer + dwBufferSize);
  781. if (pBuffer != NULL)
  782. {
  783. int iStringCount;
  784. // Walk thru the NET_DISPLAY_USER array and convert/copy the
  785. // struct and strings to GINA_USER_INFORMATION and allocate the
  786. // space from the buffer we just allocated.
  787. for (iIndex = 0; iIndex < static_cast<int>(dwEntriesRead); ++iIndex)
  788. {
  789. iStringCount = lstrlenW(pNDU[iIndex].usri1_name) + sizeof('\0');
  790. pWC -= iStringCount;
  791. CopyMemory(pWC, pNDU[iIndex].usri1_name, iStringCount * sizeof(WCHAR));
  792. pUserList[iIndex].pszName = pWC;
  793. iStringCount = lstrlenW(szComputerName) + sizeof('\0');
  794. pWC -= iStringCount;
  795. CopyMemory(pWC, szComputerName, iStringCount * sizeof(WCHAR));
  796. pUserList[iIndex].pszDomain = pWC;
  797. iStringCount = lstrlenW(pNDU[iIndex].usri1_full_name) + sizeof('\0');
  798. pWC -= iStringCount;
  799. CopyMemory(pWC, pNDU[iIndex].usri1_full_name, iStringCount * sizeof(WCHAR));
  800. pUserList[iIndex].pszFullName = pWC;
  801. pUserList[iIndex].dwFlags = pNDU[iIndex].usri1_flags;
  802. }
  803. // Return the count of entries.
  804. dwEntryCount = dwEntriesRead;
  805. // And a success.
  806. fResult = true;
  807. }
  808. else
  809. {
  810. fResult = false;
  811. }
  812. return(fResult);
  813. }
  814. // --------------------------------------------------------------------------
  815. // CUserList::Sort
  816. //
  817. // Arguments: pNDU = GINA_USER_INFORMATION list to sort.
  818. // dwEntriesRead = Number of entries in the list.
  819. //
  820. // Returns: <none>
  821. //
  822. // Purpose: Sorts the GINA_USER_INFORMATION array by display name NOT
  823. // logon name as the SAM returns the data. This is a lame n^2
  824. // algorithm that won't scale well but it's for a very limited
  825. // usage scenario. If need be this will be revised.
  826. //
  827. // History: 2000-06-08 vtan created
  828. // 2000-06-26 vtan converted to GINA_USER_INFORMATION
  829. // --------------------------------------------------------------------------
  830. void CUserList::Sort (GINA_USER_INFORMATION *pUserList, DWORD dwEntryCount)
  831. {
  832. GINA_USER_INFORMATION *pSortedList;
  833. pSortedList = static_cast<GINA_USER_INFORMATION*>(LocalAlloc(LMEM_FIXED, dwEntryCount * sizeof(GINA_USER_INFORMATION)));
  834. if (pSortedList != NULL)
  835. {
  836. int iOuter;
  837. for (iOuter = 0; iOuter < static_cast<int>(dwEntryCount); ++iOuter)
  838. {
  839. int iInner, iItem;
  840. const WCHAR *pszItem;
  841. for (iItem = -1, pszItem = NULL, iInner = 0; iInner < static_cast<int>(dwEntryCount); ++iInner)
  842. {
  843. const WCHAR *psz;
  844. psz = pUserList[iInner].pszFullName;
  845. if ((psz == NULL) || (psz[0] == L'\0'))
  846. {
  847. psz = pUserList[iInner].pszName;
  848. }
  849. if (psz != NULL)
  850. {
  851. if ((iItem == -1) || (lstrcmpiW(pszItem, psz) > 0))
  852. {
  853. iItem = iInner;
  854. pszItem = psz;
  855. }
  856. }
  857. }
  858. pSortedList[iOuter] = pUserList[iItem];
  859. pUserList[iItem].pszFullName = pUserList[iItem].pszName = NULL;
  860. }
  861. CopyMemory(pUserList, pSortedList, dwEntryCount * sizeof(GINA_USER_INFORMATION));
  862. ReleaseMemory(pSortedList);
  863. }
  864. }