// -------------------------------------------------------------------------- // Module Name: UserList.cpp // // Copyright (c) 1999-2000, Microsoft Corporation // // Class that implements the user list filtering algorithm shared by winlogon // calling into msgina and shgina (the logonocx) calling into msgina. // // History: 1999-10-30 vtan created // 1999-11-26 vtan moved from logonocx // 2000-01-31 vtan moved from Neptune to Whistler // -------------------------------------------------------------------------- #include "StandardHeader.h" #include "UserList.h" #include #include #include #include "RegistryResources.h" #include "SpecialAccounts.h" #include "SystemSettings.h" // -------------------------------------------------------------------------- // CUserList::s_SIDAdministrator // CUserList::s_SIDGuest // CUserList::s_szAdministratorsGroupName // CUserList::s_szPowerUsersGroupName // CUserList::s_szUsersGroupName // CUserList::s_szGuestsGroupName // // Purpose: Stores the localized name of the well known accounts // "Administrator" and "Guest". These accounts are determined // by SID. Also stores the localized name of the local // "Administrators" group. // // History: 2000-02-15 vtan created // 2000-03-06 vtan added Administrators group // 2001-05-10 vtan changed user strings to SID // -------------------------------------------------------------------------- unsigned char CUserList::s_SIDAdministrator[256] = { 0 }; unsigned char CUserList::s_SIDGuest[256] = { 0 }; WCHAR CUserList::s_szAdministratorsGroupName[GNLEN + sizeof('\0')] = { L'\0' }; WCHAR CUserList::s_szPowerUsersGroupName[GNLEN + sizeof('\0')] = { L'\0' }; WCHAR CUserList::s_szUsersGroupName[GNLEN + sizeof('\0')] = { L'\0' }; WCHAR CUserList::s_szGuestsGroupName[GNLEN + sizeof('\0')] = { L'\0' }; // -------------------------------------------------------------------------- // CUserList::Get // // Arguments: fRemoveGuest = Always remove the "Guest" account. // pdwReturnEntryCount = Returned number of entries. This // may be NULL. // pUserList = Buffer containing user data. This // may be NULL. // // Returns: LONG // // Purpose: Returns a filtered array of user entries from the given // server SAM. Filtering is performed here so that a common // algorithm can be applied to the list of users such that the // logon UI host can display the correct user information and // msgina can return the same number of users on the system. // // History: 1999-10-15 vtan created // 1999-10-30 vtan uses CSpecialAccounts // 1999-11-26 vtan moved from logonocx // -------------------------------------------------------------------------- LONG CUserList::Get (bool fRemoveGuest, DWORD *pdwReturnedEntryCount, GINA_USER_INFORMATION* *pReturnedUserList) { LONG lError; DWORD dwPreferredSize, dwEntryCount, dwEntriesRead; GINA_USER_INFORMATION *pUserList; NET_DISPLAY_USER *pNDU; CSpecialAccounts SpecialAccounts; pUserList = NULL; dwEntryCount = 0; // Determine the well known account names. DetermineWellKnownAccountNames(); // Allow a buffer for 100 users including their name, comments and full name. // This should be sufficient for home consumers. If the need to extend this // arises make this dynamic! dwPreferredSize = (sizeof(NET_DISPLAY_USER) + (3 * UNLEN) * s_iMaximumUserCount); pNDU = NULL; lError = NetQueryDisplayInformation(NULL, // NULL means LocalMachine 1, // query User information 0, // starting with the first user s_iMaximumUserCount, // return a max of 100 users dwPreferredSize, // preferred buffer size &dwEntriesRead, reinterpret_cast(&pNDU)); if ((ERROR_SUCCESS == lError) || (ERROR_MORE_DATA == lError)) { bool fHasCreatedAccount, fFound; DWORD dwUsernameSize; int iIndex, iAdministratorIndex; WCHAR wszUsername[UNLEN + sizeof('\0')]; // Get the current user name. dwUsernameSize = ARRAYSIZE(wszUsername); if (GetUserNameW(wszUsername, &dwUsernameSize) == FALSE) { wszUsername[0] = L'\0'; } fHasCreatedAccount = false; iAdministratorIndex = -1; for (iIndex = static_cast(dwEntriesRead - 1); iIndex >= 0; --iIndex) { PSID pSID; pSID = ConvertNameToSID(pNDU[iIndex].usri1_name); if (pSID != NULL) { // Never filter the current user. if (lstrcmpiW(pNDU[iIndex].usri1_name, wszUsername) == 0) { // If this is executed in the current user context and // that user isn't "Administrator", but is a member of // the local administrators group, then a user created // administrator account exists even though it isn't // filtered. The "Administrator" account can be removed. if ((EqualSid(pSID, s_SIDAdministrator) == FALSE) && IsUserMemberOfLocalAdministrators(pNDU[iIndex].usri1_name)) { fHasCreatedAccount = true; if (iAdministratorIndex >= 0) { DeleteEnumerateUsers(pNDU, dwEntriesRead, iAdministratorIndex); iAdministratorIndex = -1; } } } else { // If the account is // 1) disabled // 2) locked out // 3) a special account (see CSpecialAccounts) // 4) "Guest" and fRemoveGuest is true // 5) "Administrator" and has created another account // and does not always include "Administrator" and // "Administrator is not logged on // Then filter the account out. if (((pNDU[iIndex].usri1_flags & UF_ACCOUNTDISABLE) != 0) || ((pNDU[iIndex].usri1_flags & UF_LOCKOUT) != 0) || SpecialAccounts.AlwaysExclude(pNDU[iIndex].usri1_name) || (fRemoveGuest && (EqualSid(pSID, s_SIDGuest) != FALSE)) || ((EqualSid(pSID, s_SIDAdministrator) != FALSE) && fHasCreatedAccount && !SpecialAccounts.AlwaysInclude(pNDU[iIndex].usri1_name) && !IsUserLoggedOn(pNDU[iIndex].usri1_name, NULL))) { DeleteEnumerateUsers(pNDU, dwEntriesRead, iIndex); // Account for indices being changed. // If this index wasn't set previously it just goes more negative. // If it was set we know it can never be below zero. --iAdministratorIndex; } // If the account should always be included then do it. // Guest is not a user created account so fHasCreatedAccount // must not be set if this account is seen. else if (!SpecialAccounts.AlwaysInclude(pNDU[iIndex].usri1_name)) { // If safe mode then filter accounts that are not members of the // local administrators group. if (CSystemSettings::IsSafeMode()) { if (!IsUserMemberOfLocalAdministrators(pNDU[iIndex].usri1_name)) { DeleteEnumerateUsers(pNDU, dwEntriesRead, iIndex); --iAdministratorIndex; } } else if (EqualSid(pSID, s_SIDAdministrator) != FALSE) { if (!IsUserLoggedOn(pNDU[iIndex].usri1_name, NULL)) { // Otherwise if the account name is "Administrator" and another // account has been created then this account needs to be removed // from the list. If another account has not been seen then // remember this index so that if another account is seen this // account can be removed. if (fHasCreatedAccount) { DeleteEnumerateUsers(pNDU, dwEntriesRead, iIndex); --iAdministratorIndex; } else { iAdministratorIndex = iIndex; } } } else if (EqualSid(pSID, s_SIDGuest) == FALSE) { // If the account name is NOT "Administrator" then check the // account group membership. If the account is a member of the // local administrators group then the "Administrator" account // can be removed. if (IsUserMemberOfLocalAdministrators(pNDU[iIndex].usri1_name)) { fHasCreatedAccount = true; if (iAdministratorIndex >= 0) { DeleteEnumerateUsers(pNDU, dwEntriesRead, iAdministratorIndex); iAdministratorIndex = -1; } } if (!IsUserMemberOfLocalKnownGroup(pNDU[iIndex].usri1_name)) { DeleteEnumerateUsers(pNDU, dwEntriesRead, iIndex); --iAdministratorIndex; } } } } (HLOCAL)LocalFree(pSID); } } if (!ParseDisplayInformation(pNDU, dwEntriesRead, pUserList, dwEntryCount)) { lError = ERROR_OUTOFMEMORY; pUserList = NULL; dwEntryCount = 0; } (NET_API_STATUS)NetApiBufferFree(pNDU); if (ERROR_SUCCESS == lError) { // Sort the user list. Typically this has come back alphabetized by the // SAM. However, the SAM sorts by logon name and not by display name. // This needs to be sorted by display name. Sort(pUserList, dwEntryCount); // The guest account should be put at the end of this list. This // is a simple case of find the guest account (by localized name) and // sliding all the entries down and inserting the guest at the end. for (fFound = false, iIndex = 0; !fFound && (iIndex < static_cast(dwEntryCount)); ++iIndex) { PSID pSID; pSID = ConvertNameToSID(pUserList[iIndex].pszName); if (pSID != NULL) { fFound = (EqualSid(pSID, s_SIDGuest) != FALSE); if (fFound) { GINA_USER_INFORMATION gui; MoveMemory(&gui, &pUserList[iIndex], sizeof(gui)); MoveMemory(&pUserList[iIndex], &pUserList[iIndex + 1], (dwEntryCount - iIndex - 1) * sizeof(pUserList[0])); MoveMemory(&pUserList[dwEntryCount - 1], &gui, sizeof(gui)); } (HLOCAL)LocalFree(pSID); } } } } if (pReturnedUserList != NULL) { *pReturnedUserList = pUserList; } else { ReleaseMemory(pUserList); } if (pdwReturnedEntryCount != NULL) { *pdwReturnedEntryCount = dwEntryCount; } return(lError); } // -------------------------------------------------------------------------- // CLogonDialog::IsUserLoggedOn // // Arguments: pszUsername = User name. // pszDomain = User domain. // // Returns: bool // // Purpose: Use WindowStation APIs in terminal services to determine if // a given user is logged onto this machine. It will not query // remote terminal servers. // // Windowstations must be in the active or disconnected state. // // History: 2000-02-28 vtan created // 2000-05-30 vtan moved from CWLogonDialog.cpp // -------------------------------------------------------------------------- bool CUserList::IsUserLoggedOn (const WCHAR *pszUsername, const WCHAR *pszDomain) { bool fResult; WCHAR szDomain[DNLEN + sizeof('\0')]; fResult = false; // If no domain is supplied then use the computer's name. if ((pszDomain == NULL) || (pszDomain[0] == L'\0')) { DWORD dwDomainSize; dwDomainSize = ARRAYSIZE(szDomain); if (GetComputerNameW(szDomain, &dwDomainSize) != FALSE) { pszDomain = szDomain; } } // If no domain is supplied and the computer's name cannot be determined // then this API fails. A user name must also be supplied. if ((pszUsername != NULL) && (pszDomain != NULL)) { HANDLE hServer; PLOGONID pLogonID, pLogonIDs; ULONG ul, ulEntries; // Open a connection to terminal services and get the number of sessions. hServer = WinStationOpenServerW(reinterpret_cast(SERVERNAME_CURRENT)); if (hServer != NULL) { if (WinStationEnumerate(hServer, &pLogonIDs, &ulEntries) != FALSE) { // Iterate the sessions looking for active and disconnected sessions only. // Then match the user name and domain (case INsensitive) for a result. for (ul = 0, pLogonID = pLogonIDs; !fResult && (ul < ulEntries); ++ul, ++pLogonID) { if ((pLogonID->State == State_Active) || (pLogonID->State == State_Disconnected)) { ULONG ulReturnLength; WINSTATIONINFORMATIONW winStationInformation; if (WinStationQueryInformationW(hServer, pLogonID->LogonId, WinStationInformation, &winStationInformation, sizeof(winStationInformation), &ulReturnLength) != FALSE) { fResult = ((lstrcmpiW(pszUsername, winStationInformation.UserName) == 0) && (lstrcmpiW(pszDomain, winStationInformation.Domain) == 0)); } } } // Free any resources used. (BOOLEAN)WinStationFreeMemory(pLogonIDs); } (BOOLEAN)WinStationCloseServer(hServer); } } return(fResult); } // -------------------------------------------------------------------------- // CUserList::IsInteractiveLogonAllowed // // Arguments: pszUsername = User name. // // Returns: int // // Purpose: Determines whether the SeDenyInteractiveLogonRight is // assigned into the given user. Returns -1 if the state cannot // be determined due to some error. Otherwise returns 0 if the // the right is assigned and != 0 && != -1 if not. // // One final check is made on personal for a user name that // matches DOMAIN_USER_RID_ADMIN. // // History: 2000-08-15 vtan created // -------------------------------------------------------------------------- int CUserList::IsInteractiveLogonAllowed (const WCHAR *pszUsername) { int iResult; LSA_HANDLE hLSA; UNICODE_STRING strDenyInteractiveLogonRight; OBJECT_ATTRIBUTES objectAttributes; iResult = -1; RtlInitUnicodeString(&strDenyInteractiveLogonRight, SE_DENY_INTERACTIVE_LOGON_NAME); InitializeObjectAttributes(&objectAttributes, NULL, 0, NULL, NULL); if (NT_SUCCESS(LsaOpenPolicy(NULL, &objectAttributes, POLICY_LOOKUP_NAMES, &hLSA))) { SID_NAME_USE eUse; DWORD dwSIDSize, dwReferencedDomainSize; PSID pSID; WCHAR szReferencedDomain[CNLEN + sizeof('\0')]; dwSIDSize = 0; dwReferencedDomainSize = ARRAYSIZE(szReferencedDomain); (BOOL)LookupAccountNameW(NULL, pszUsername, NULL, &dwSIDSize, szReferencedDomain, &dwReferencedDomainSize, &eUse); pSID = static_cast(LocalAlloc(LMEM_FIXED, dwSIDSize)); if (pSID != NULL) { if (LookupAccountNameW(NULL, pszUsername, pSID, &dwSIDSize, szReferencedDomain, &dwReferencedDomainSize, &eUse) != FALSE) { NTSTATUS status; ULONG ulIndex, ulCountOfRights; PLSA_UNICODE_STRING pUserRights; status = LsaEnumerateAccountRights(hLSA, pSID, &pUserRights, &ulCountOfRights); if (NT_SUCCESS(status)) { bool fFound; for (fFound = false, ulIndex = 0; !fFound && (ulIndex < ulCountOfRights); ++ulIndex) { fFound = (RtlEqualUnicodeString(&strDenyInteractiveLogonRight, pUserRights + ulIndex, TRUE) != FALSE); } iResult = fFound ? 0 : 1; TSTATUS(LsaFreeMemory(pUserRights)); } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) { iResult = 1; } } (HLOCAL)LocalFree(pSID); } TSTATUS(LsaClose(hLSA)); } if (IsOS(OS_PERSONAL) && !CSystemSettings::IsSafeMode()) { PSID pSID; pSID = ConvertNameToSID(pszUsername); if (pSID != NULL) { if (EqualSid(pSID, s_SIDAdministrator) != FALSE) { iResult = 0; } (HLOCAL)LocalFree(pSID); } } return(iResult); } PSID CUserList::ConvertNameToSID (const WCHAR *pszUsername) { PSID pSID; DWORD dwSIDSize, dwDomainSize; SID_NAME_USE eUse; pSID = NULL; dwSIDSize = dwDomainSize = 0; (BOOL)LookupAccountNameW(NULL, pszUsername, NULL, &dwSIDSize, NULL, &dwDomainSize, NULL); if ((dwSIDSize != 0) && (dwDomainSize != 0)) { WCHAR *pszDomain; pszDomain = static_cast(LocalAlloc(LMEM_FIXED, dwDomainSize * sizeof(WCHAR))); if (pszDomain != NULL) { pSID = static_cast(LocalAlloc(LMEM_FIXED, dwSIDSize)); if (pSID != NULL) { if (LookupAccountName(NULL, pszUsername, pSID, &dwSIDSize, pszDomain, &dwDomainSize, &eUse) == FALSE) { (HLOCAL)LocalFree(pSID); pSID = NULL; } } (HLOCAL)LocalFree(pszDomain); } } return(pSID); } // -------------------------------------------------------------------------- // CUserList::IsUserMemberOfLocalAdministrators // // Arguments: pszName = User name to test. // // Returns: bool // // Purpose: Returns whether the given user is a member of the local // Administrators group. // // History: 2000-03-28 vtan created // -------------------------------------------------------------------------- bool CUserList::IsUserMemberOfLocalAdministrators (const WCHAR *pszName) { bool fIsAnAdministrator; DWORD dwGroupEntriesRead, dwGroupTotalEntries; LOCALGROUP_USERS_INFO_0 *pLocalGroupUsersInfo; fIsAnAdministrator = false; pLocalGroupUsersInfo = NULL; if (NetUserGetLocalGroups(NULL, pszName, 0, LG_INCLUDE_INDIRECT, (LPBYTE*)&pLocalGroupUsersInfo, MAX_PREFERRED_LENGTH, &dwGroupEntriesRead, &dwGroupTotalEntries) == NERR_Success) { int iIndexGroup; LOCALGROUP_USERS_INFO_0 *pLGUI; for (iIndexGroup = 0, pLGUI = pLocalGroupUsersInfo; !fIsAnAdministrator && (iIndexGroup < static_cast(dwGroupEntriesRead)); ++iIndexGroup, ++pLGUI) { fIsAnAdministrator = (lstrcmpiW(pLGUI->lgrui0_name, s_szAdministratorsGroupName) == 0); } } else { fIsAnAdministrator = true; } if (pLocalGroupUsersInfo != NULL) { TW32(NetApiBufferFree(pLocalGroupUsersInfo)); } return(fIsAnAdministrator); } // -------------------------------------------------------------------------- // CUserList::IsUserMemberOfLocalKnownGroup // // Arguments: pszName = User name to test. // // Returns: bool // // Purpose: Returns whether the given user is a member of a local known // group. Membership of a known group returns true. Membership // of only groups that are not known returns false. // // History: 2000-06-29 vtan created // -------------------------------------------------------------------------- bool CUserList::IsUserMemberOfLocalKnownGroup (const WCHAR *pszName) { bool fIsMember; DWORD dwGroupEntriesRead, dwGroupTotalEntries; LOCALGROUP_USERS_INFO_0 *pLocalGroupUsersInfo; fIsMember = true; pLocalGroupUsersInfo = NULL; if (NetUserGetLocalGroups(NULL, pszName, 0, LG_INCLUDE_INDIRECT, (LPBYTE*)&pLocalGroupUsersInfo, MAX_PREFERRED_LENGTH, &dwGroupEntriesRead, &dwGroupTotalEntries) == NERR_Success) { int iIndexGroup; LOCALGROUP_USERS_INFO_0 *pLGUI; // Assume the worst. As soon as a known group is found this will terminate the loop. fIsMember = false; for (iIndexGroup = 0, pLGUI = pLocalGroupUsersInfo; !fIsMember && (iIndexGroup < static_cast(dwGroupEntriesRead)); ++iIndexGroup, ++pLGUI) { fIsMember = ((lstrcmpiW(pLGUI->lgrui0_name, s_szAdministratorsGroupName) == 0) || (lstrcmpiW(pLGUI->lgrui0_name, s_szPowerUsersGroupName) == 0) || (lstrcmpiW(pLGUI->lgrui0_name, s_szUsersGroupName) == 0) || (lstrcmpiW(pLGUI->lgrui0_name, s_szGuestsGroupName) == 0)); } } if (pLocalGroupUsersInfo != NULL) { TW32(NetApiBufferFree(pLocalGroupUsersInfo)); } return(fIsMember); } // -------------------------------------------------------------------------- // CUserList::DeleteEnumerateUsers // // Arguments: pNDU = NET_DISPLAY_USER array to delete from. // dwEntriesRead = Number of entries in the array. // iIndex = Index to delete. // // Returns: // // Purpose: Deletes the given array index contents from the array by // sliding down the elements and zeroing the last entry. // // History: 1999-10-16 vtan created // 1999-11-26 vtan moved from logonocx // -------------------------------------------------------------------------- void CUserList::DeleteEnumerateUsers (NET_DISPLAY_USER *pNDU, DWORD& dwEntriesRead, int iIndex) { int iIndiciesToMove; iIndiciesToMove = static_cast(dwEntriesRead - 1) - iIndex; if (iIndiciesToMove != 0) { MoveMemory(&pNDU[iIndex], &pNDU[iIndex + 1], iIndiciesToMove * sizeof(*pNDU)); } ZeroMemory(&pNDU[--dwEntriesRead], sizeof(*pNDU)); } // -------------------------------------------------------------------------- // CUserList::DetermineWellKnownAccountNames // // Arguments: // // Returns: // // Purpose: Determines the string for the local Administrator and Guest // accounts by getting the user list from the local SAM and // looking up the SID corresponding with the iterated user names // and checking the SID for the RID that is desired. // // The main loop structure mimics the filter function. // // History: 2000-02-15 vtan created // -------------------------------------------------------------------------- void CUserList::DetermineWellKnownAccountNames (void) { static bool s_fCachedWellKnownAccountNames = false; // If the well known account names haven't been determined yet // then do this. But only do this once. if (!s_fCachedWellKnownAccountNames) { USER_MODALS_INFO_2 *pUMI; PSID pSID; DWORD dwNameSize, dwDomainSize; SID_NAME_USE eUse; WCHAR szDomain[DNLEN + sizeof('\0')]; // Build the SID for the built-in local administrator // and built-in local guest accounts. if (NetUserModalsGet(NULL, 2, (LPBYTE*)&pUMI) == NERR_Success) { unsigned char ucSubAuthorityCount; ucSubAuthorityCount = *GetSidSubAuthorityCount(pUMI->usrmod2_domain_id); if (GetSidLengthRequired(ucSubAuthorityCount + 1) <= sizeof(s_SIDAdministrator)) { if (CopySid(GetSidLengthRequired(ucSubAuthorityCount + 1), s_SIDAdministrator, pUMI->usrmod2_domain_id) != FALSE) { *GetSidSubAuthority(s_SIDAdministrator, ucSubAuthorityCount) = DOMAIN_USER_RID_ADMIN; *GetSidSubAuthorityCount(s_SIDAdministrator) = ucSubAuthorityCount + 1; } } else { ZeroMemory(s_SIDAdministrator, sizeof(s_SIDAdministrator)); } if (GetSidLengthRequired(ucSubAuthorityCount + 1) <= sizeof(s_SIDGuest)) { if (CopySid(GetSidLengthRequired(ucSubAuthorityCount + 1), s_SIDGuest, pUMI->usrmod2_domain_id) != FALSE) { *GetSidSubAuthority(s_SIDGuest, ucSubAuthorityCount) = DOMAIN_USER_RID_GUEST; *GetSidSubAuthorityCount(s_SIDGuest) = ucSubAuthorityCount + 1; } } else { ZeroMemory(s_SIDAdministrator, sizeof(s_SIDAdministrator)); } (NET_API_STATUS)NetApiBufferFree(pUMI); } // Now determine the local administrators group name. static SID_IDENTIFIER_AUTHORITY sSystemSidAuthority = SECURITY_NT_AUTHORITY; if (NT_SUCCESS(RtlAllocateAndInitializeSid(&sSystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSID))) { dwNameSize = ARRAYSIZE(s_szAdministratorsGroupName); dwDomainSize = ARRAYSIZE(szDomain); TBOOL(LookupAccountSidW(NULL, pSID, s_szAdministratorsGroupName, &dwNameSize, szDomain, &dwDomainSize, &eUse)); (void*)RtlFreeSid(pSID); } // Power Users if (NT_SUCCESS(RtlAllocateAndInitializeSid(&sSystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, 0, &pSID))) { dwNameSize = ARRAYSIZE(s_szPowerUsersGroupName); dwDomainSize = ARRAYSIZE(szDomain); (BOOL)LookupAccountSidW(NULL, pSID, s_szPowerUsersGroupName, &dwNameSize, szDomain, &dwDomainSize, &eUse); (void*)RtlFreeSid(pSID); } // Users if (NT_SUCCESS(RtlAllocateAndInitializeSid(&sSystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &pSID))) { dwNameSize = ARRAYSIZE(s_szUsersGroupName); dwDomainSize = ARRAYSIZE(szDomain); TBOOL(LookupAccountSidW(NULL, pSID, s_szUsersGroupName, &dwNameSize, szDomain, &dwDomainSize, &eUse)); (void*)RtlFreeSid(pSID); } // Guests if (NT_SUCCESS(RtlAllocateAndInitializeSid(&sSystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, 0, 0, 0, 0, 0, 0, &pSID))) { dwNameSize = ARRAYSIZE(s_szGuestsGroupName); dwDomainSize = ARRAYSIZE(szDomain); TBOOL(LookupAccountSidW(NULL, pSID, s_szGuestsGroupName, &dwNameSize, szDomain, &dwDomainSize, &eUse)); (void*)RtlFreeSid(pSID); } // Don't do this again. s_fCachedWellKnownAccountNames = true; } } // -------------------------------------------------------------------------- // CUserList::ParseDisplayInformation // // Arguments: pNDU = NET_DISPLAY_USER list to parse. // dwEntriesRead = Number of entries in NDU list. // pUserList = GINA_USER_INFORMATION pointer returned. // dwEntryCount = Number of entries in GUI list. // // Returns: bool // // Purpose: Converts NET_DISPLAY_USER array to GINA_USER_INFORMATION // array so that information can be added or removed as desired // from the final information returned to the caller. // // History: 2000-06-26 vtan created // -------------------------------------------------------------------------- bool CUserList::ParseDisplayInformation (NET_DISPLAY_USER *pNDU, DWORD dwEntriesRead, GINA_USER_INFORMATION*& pUserList, DWORD& dwEntryCount) { bool fResult; DWORD dwBufferSize, dwComputerNameSize; int iIndex; unsigned char *pBuffer; WCHAR *pWC; WCHAR szComputerName[CNLEN + sizeof('\0')]; // Get the local computer name. This is the local domain. dwComputerNameSize = ARRAYSIZE(szComputerName); if (GetComputerNameW(szComputerName, &dwComputerNameSize) == FALSE) { szComputerName[0] = L'\0'; } // Calculate the total size of the buffer required based on the number of // entries and the size of a struct and the length of the strings required. // Append any additions to the below this loop. dwBufferSize = 0; for (iIndex = static_cast(dwEntriesRead - 1); iIndex >= 0; --iIndex) { dwBufferSize += sizeof(GINA_USER_INFORMATION); dwBufferSize += (lstrlenW(pNDU[iIndex].usri1_name) + sizeof('\0')) * sizeof(WCHAR); dwBufferSize += (lstrlenW(szComputerName) + sizeof('\0')) * sizeof(WCHAR); dwBufferSize += (lstrlenW(pNDU[iIndex].usri1_full_name) + sizeof('\0')) * sizeof(WCHAR); } // Allocate the buffer. Start allocating structs from the start of the // buffer and allocate strings from the end of the buffer. Uses pUserList // to allocate structs and pWC to allocate strings. pBuffer = static_cast(LocalAlloc(LMEM_FIXED, dwBufferSize)); pUserList = reinterpret_cast(pBuffer); pWC = reinterpret_cast(pBuffer + dwBufferSize); if (pBuffer != NULL) { int iStringCount; // Walk thru the NET_DISPLAY_USER array and convert/copy the // struct and strings to GINA_USER_INFORMATION and allocate the // space from the buffer we just allocated. for (iIndex = 0; iIndex < static_cast(dwEntriesRead); ++iIndex) { iStringCount = lstrlenW(pNDU[iIndex].usri1_name) + sizeof('\0'); pWC -= iStringCount; CopyMemory(pWC, pNDU[iIndex].usri1_name, iStringCount * sizeof(WCHAR)); pUserList[iIndex].pszName = pWC; iStringCount = lstrlenW(szComputerName) + sizeof('\0'); pWC -= iStringCount; CopyMemory(pWC, szComputerName, iStringCount * sizeof(WCHAR)); pUserList[iIndex].pszDomain = pWC; iStringCount = lstrlenW(pNDU[iIndex].usri1_full_name) + sizeof('\0'); pWC -= iStringCount; CopyMemory(pWC, pNDU[iIndex].usri1_full_name, iStringCount * sizeof(WCHAR)); pUserList[iIndex].pszFullName = pWC; pUserList[iIndex].dwFlags = pNDU[iIndex].usri1_flags; } // Return the count of entries. dwEntryCount = dwEntriesRead; // And a success. fResult = true; } else { fResult = false; } return(fResult); } // -------------------------------------------------------------------------- // CUserList::Sort // // Arguments: pNDU = GINA_USER_INFORMATION list to sort. // dwEntriesRead = Number of entries in the list. // // Returns: // // Purpose: Sorts the GINA_USER_INFORMATION array by display name NOT // logon name as the SAM returns the data. This is a lame n^2 // algorithm that won't scale well but it's for a very limited // usage scenario. If need be this will be revised. // // History: 2000-06-08 vtan created // 2000-06-26 vtan converted to GINA_USER_INFORMATION // -------------------------------------------------------------------------- void CUserList::Sort (GINA_USER_INFORMATION *pUserList, DWORD dwEntryCount) { GINA_USER_INFORMATION *pSortedList; pSortedList = static_cast(LocalAlloc(LMEM_FIXED, dwEntryCount * sizeof(GINA_USER_INFORMATION))); if (pSortedList != NULL) { int iOuter; for (iOuter = 0; iOuter < static_cast(dwEntryCount); ++iOuter) { int iInner, iItem; const WCHAR *pszItem; for (iItem = -1, pszItem = NULL, iInner = 0; iInner < static_cast(dwEntryCount); ++iInner) { const WCHAR *psz; psz = pUserList[iInner].pszFullName; if ((psz == NULL) || (psz[0] == L'\0')) { psz = pUserList[iInner].pszName; } if (psz != NULL) { if ((iItem == -1) || (lstrcmpiW(pszItem, psz) > 0)) { iItem = iInner; pszItem = psz; } } } pSortedList[iOuter] = pUserList[iItem]; pUserList[iItem].pszFullName = pUserList[iItem].pszName = NULL; } CopyMemory(pUserList, pSortedList, dwEntryCount * sizeof(GINA_USER_INFORMATION)); ReleaseMemory(pSortedList); } }