Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

780 lines
25 KiB

#include "stdafx.h"
#include "userinfo.h"
#pragma hdrstop
/*******************************************************************
CUserInfo implementation
*******************************************************************/
CUserInfo::CUserInfo()
{
m_fHaveExtraUserInfo = FALSE;
m_psid = NULL;
}
CUserInfo::~CUserInfo()
{
if (m_psid != NULL)
LocalFree(m_psid);
ZeroPassword();
}
HRESULT CUserInfo::Reload(BOOL fLoadExtraInfo /* = NULL */)
{
// Initialize the structure and add it to the head of the list
DWORD cchUsername = ARRAYSIZE(m_szUsername);
DWORD cchDomain = ARRAYSIZE(m_szDomain);
if (LookupAccountSid(NULL, m_psid, m_szUsername, &cchUsername, m_szDomain, &cchDomain, &m_sUse))
{
m_fHaveExtraUserInfo = FALSE;
if (fLoadExtraInfo)
GetExtraUserInfo();
SetUserType();
SetAccountDisabled();
return SetLocalGroups();
}
return E_FAIL;
}
HRESULT CUserInfo::SetLocalGroups()
{
TCHAR szDomainUser[MAX_DOMAIN + MAX_USER + 2];
::MakeDomainUserString(m_szDomain, m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
DWORD dwEntriesRead;
DWORD dwTotalEntries;
BOOL fMore = TRUE;
DWORD iNextGroupName = 0;
BOOL fAddElipses = FALSE;
HRESULT hr = S_OK;
while (fMore)
{
LOCALGROUP_USERS_INFO_0* prglgrui0;
NET_API_STATUS status = NetUserGetLocalGroups(NULL, szDomainUser, 0, 0,
(BYTE**) &prglgrui0, 2048,
&dwEntriesRead, &dwTotalEntries);
if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
{
for (DWORD i = 0; i < dwEntriesRead; i++)
{
DWORD iThisGroupName = iNextGroupName;
iNextGroupName += lstrlen(prglgrui0[i].lgrui0_name) + 2;
if (iNextGroupName < (ARRAYSIZE(m_szGroups) - 1))
{
lstrcpy(&m_szGroups[iThisGroupName], prglgrui0[i].lgrui0_name);
lstrcpy(&m_szGroups[iNextGroupName - 2], TEXT("; "));
}
else
{
fAddElipses = TRUE;
if (iThisGroupName + 3 >= (ARRAYSIZE(m_szGroups)))
iThisGroupName -= 3;
lstrcpy(&m_szGroups[iThisGroupName], TEXT("..."));
// No need to read more; we're out o' buffer
fMore = FALSE;
}
}
NetApiBufferFree((void*) prglgrui0);
}
else
{
hr = E_FAIL;
}
if (status != ERROR_MORE_DATA)
{
fMore = FALSE;
}
}
// There is an extra ';' at the end. Nuke it
if (!fAddElipses && ((iNextGroupName - 2) < (ARRAYSIZE(m_szGroups))))
{
m_szGroups[iNextGroupName - 2] = TEXT('\0');
}
// Absolutely guarantee the string ends in a null
m_szGroups[ARRAYSIZE(m_szGroups) - 1] = TEXT('\0');
return hr;
}
HRESULT CUserInfo::Load(PSID psid, BOOL fLoadExtraInfo /* = NULL */)
{
CUserInfo(); // Nuke the record first
// Make a copy of the SID
DWORD cbSid = GetLengthSid(psid);
m_psid = (PSID) LocalAlloc(NULL, cbSid);
if (!m_psid)
return E_OUTOFMEMORY;
CopySid(cbSid, m_psid, psid);
return Reload(fLoadExtraInfo);
}
HRESULT CUserInfo::Create(HWND hwndError, GROUPPSEUDONYM grouppseudonym)
{
NET_API_STATUS status = NERR_Success;
CWaitCursor cur;
HRESULT hr = E_FAIL;
if (m_userType == CUserInfo::LOCALUSER)
{
// Fill in the big, ugly structure containing information about our new user
USER_INFO_2 usri2 = {0};
usri2.usri2_name = T2W(m_szUsername);
// Reveal the password
RevealPassword();
usri2.usri2_password = T2W(m_szPasswordBuffer);
usri2.usri2_priv = USER_PRIV_USER;
usri2.usri2_comment = T2W(m_szComment);
if (m_szPasswordBuffer[0] == TEXT('\0'))
usri2.usri2_flags = UF_NORMAL_ACCOUNT | UF_SCRIPT | UF_PASSWD_NOTREQD;
else
usri2.usri2_flags = UF_NORMAL_ACCOUNT | UF_SCRIPT;
usri2.usri2_full_name = T2W(m_szFullName);
usri2.usri2_acct_expires = TIMEQ_FOREVER;
usri2.usri2_max_storage = USER_MAXSTORAGE_UNLIMITED;
TCHAR szCountryCode[7];
if (0 < GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ICOUNTRY, szCountryCode, ARRAYSIZE(szCountryCode)))
{
usri2.usri2_country_code = (DWORD) StrToLong(szCountryCode);
}
usri2.usri2_code_page = GetACP();
// Create the user
status = NetUserAdd(NULL, 2, (BYTE*) &usri2, NULL);
// Hide the password
HidePassword();
switch (status)
{
case NERR_Success:
hr = S_OK;
break;
case NERR_PasswordTooShort:
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
IDS_USR_CREATE_PASSWORDTOOSHORT_ERROR, MB_ICONERROR | MB_OK);
break;
case NERR_GroupExists:
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
IDS_USR_CREATE_GROUPEXISTS_ERROR, MB_ICONERROR | MB_OK);
break;
case NERR_UserExists:
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
IDS_USR_CREATE_USEREXISTS_ERROR, MB_ICONERROR | MB_OK,
m_szUsername);
break;
default:
{
TCHAR szMessage[512];
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD) status, 0, szMessage, ARRAYSIZE(szMessage), NULL))
LoadString(g_hinst, IDS_ERR_UNEXPECTED, szMessage, ARRAYSIZE(szMessage));
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION, IDS_USERCREATE_GENERICERROR, MB_ICONERROR | MB_OK, szMessage);
break;
}
}
}
else
{
hr = S_OK; // m_userType == DOMAINUSER or GROUP
}
if (SUCCEEDED(hr))
{
hr = ChangeLocalGroups(hwndError, grouppseudonym);
if (SUCCEEDED(hr))
{
// User type may have been updated by ChangeLocalGroups - // relect that!
SetUserType();
}
}
return hr;
}
HRESULT CUserInfo::Remove()
{
CWaitCursor cur;
if (m_userType == CUserInfo::LOCALUSER)
{
// Try to actually remove this local user (this may fail!)
NET_API_STATUS status = NetUserDel(NULL, m_szUsername);
if (status != NERR_Success)
{
return E_FAIL;
}
}
else
{
// We can only delete local users. For all others the best we can do is
// remove them from all local groups
return RemoveFromLocalGroups();
}
return S_OK;
}
HRESULT CUserInfo::InitializeForNewUser()
{
CUserInfo(); // Nuke the record first
m_fHaveExtraUserInfo = TRUE;
m_sUse = SidTypeUser;
m_userType = LOCALUSER;
return S_OK;
}
HRESULT CUserInfo::RemoveFromLocalGroups()
{
// Create a data structure we'll need to pass to NetLocalGroupxxx functions
TCHAR szDomainUser[MAX_USER + MAX_DOMAIN + 2];
::MakeDomainUserString(m_szDomain, m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
LOCALGROUP_MEMBERS_INFO_3 rglgrmi3[] = {{szDomainUser}};
// Try and remove the user/group from ALL local groups. The reason
// for this is the NetUserGetLocalGroups won't work for groups, even
// well-known ones. For instance, it will fail for "Everyone" even
// though "Everyone" may very well belong to local groups.
DWORD_PTR dwResumeHandle = 0;
BOOL fMoreData = TRUE;
while (fMoreData)
{
DWORD dwEntriesRead;
DWORD dwTotalEntries;
LOCALGROUP_INFO_0* plgrpi0 = NULL;
NET_API_STATUS status = NetLocalGroupEnum(NULL, 0, (BYTE**)&plgrpi0, 8192,
&dwEntriesRead, &dwTotalEntries, &dwResumeHandle);
if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
{
for (DWORD i = 0; i < dwEntriesRead; i ++)
{
status = NetLocalGroupDelMembers(NULL, plgrpi0[i].lgrpi0_name, 3,
(BYTE*) rglgrmi3, ARRAYSIZE(rglgrmi3));
}
if (dwEntriesRead == dwTotalEntries)
{
fMoreData = FALSE;
}
NetApiBufferFree(plgrpi0);
}
else
{
fMoreData = FALSE;
}
}
return S_OK;
}
HRESULT CUserInfo::SetUserType()
{
TCHAR szComputerName[MAX_COMPUTERNAME + 1];
DWORD cchComputerName = ARRAYSIZE(szComputerName);
::GetComputerName(szComputerName, &cchComputerName);
// Figure out what type of user we're talking about
if ((m_sUse == SidTypeWellKnownGroup) || (m_sUse == SidTypeGroup))
{
m_userType = GROUP;
}
else
{
// User type - see if this user is a local one
if ((m_szDomain[0] == TEXT('\0')) ||
(StrCmpI(m_szDomain, szComputerName) == 0))
{
m_userType = LOCALUSER; // Local user
}
else
{
m_userType = DOMAINUSER; // User is a network one
}
}
return S_OK;
}
HRESULT CUserInfo::SetAccountDisabled()
{
m_fAccountDisabled = FALSE;
USER_INFO_1* pusri1 = NULL;
NET_API_STATUS status = NetUserGetInfo(NULL, T2W(m_szUsername), 1, (BYTE**)&pusri1);
if (NERR_Success == status)
{
if (pusri1->usri1_flags & UF_ACCOUNTDISABLE)
{
m_fAccountDisabled = TRUE;
}
NetApiBufferFree(pusri1);
}
return S_OK;
}
HRESULT CUserInfo::GetExtraUserInfo()
{
USES_CONVERSION;
CWaitCursor cur;
if (!m_fHaveExtraUserInfo)
{
NET_API_STATUS status;
USER_INFO_11* pusri11 = NULL;
// Even if we fail to the info, we only want to try once since it may take a long time
m_fHaveExtraUserInfo = TRUE;
// Get the name of the domain's DC if we aren't talking about a local user
#ifdef _0 // Turns out this is REALLY slow to fail if the DsGetDcName call fails
if (m_userType != LOCALUSER)
{
DOMAIN_CONTROLLER_INFO* pDCInfo;
DWORD dwErr = DsGetDcName(NULL, m_szDomain, NULL, NULL, DS_IS_FLAT_NAME, &pDCInfo);
if (dwErr != NO_ERROR)
return E_FAIL;
// Get the user's detailed information (we really need full name and comment)
// Need to use level 11 here since this allows a domain user to query their
// information
status = NetUserGetInfo(T2W(pDCInfo->DomainControllerName), T2W(m_szUsername), 11, (BYTE**)&pusri11);
NetApiBufferFree(pDCInfo);
}
else
#endif //0
{
status = NetUserGetInfo(NULL, T2W(m_szUsername), 11, (BYTE**)&pusri11);
}
if (status != NERR_Success)
return E_FAIL;
StrCpyN(m_szComment, W2T(pusri11->usri11_comment), ARRAYSIZE(m_szComment));
StrCpyN(m_szFullName, W2T(pusri11->usri11_full_name), ARRAYSIZE(m_szFullName));
NetApiBufferFree(pusri11);
}
return S_OK;
}
// ChangeLocalGroups
// Removes the specified user from all current local groups and adds them to the
// SINGLE local group specified in pUserInfo->szGroups
HRESULT CUserInfo::ChangeLocalGroups(HWND hwndError, GROUPPSEUDONYM grouppseudonym)
{
// First, remove the user from all existing local groups
HRESULT hr = RemoveFromLocalGroups();
if (SUCCEEDED(hr))
{
TCHAR szDomainAndUser[MAX_USER + MAX_DOMAIN + 2];
::MakeDomainUserString(m_szDomain, m_szUsername, szDomainAndUser, ARRAYSIZE(szDomainAndUser));
// Create a data structure we'll need to pass to NetLocalGroupxxx functions
LOCALGROUP_MEMBERS_INFO_3 rglgrmi3[] = {{szDomainAndUser}};
// Now add the user to the SINGLE localgroup that should be specified in
// m_szGroups; Assert this is the case!
NET_API_STATUS status = NetLocalGroupAddMembers(NULL, T2W(m_szGroups), 3,
(BYTE*) rglgrmi3, ARRAYSIZE(rglgrmi3));
if (status == NERR_Success)
{
// We may now need to get the user's SID. This happens if we are
// changing local groups for a domain user and we couldn't read their
// SID since they weren't in the local SAM.
DWORD cchDomain = ARRAYSIZE(m_szDomain);
hr = ::AttemptLookupAccountName(szDomainAndUser, &m_psid, m_szDomain, &cchDomain, &m_sUse);
if (FAILED(hr))
{
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
IDS_USR_CREATE_MISC_ERROR, MB_ICONERROR | MB_OK);
}
}
else
{
switch(status)
{
case ERROR_NO_SUCH_MEMBER:
{
switch (grouppseudonym)
{
case RESTRICTED:
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
IDS_BADRESTRICTEDUSER, MB_ICONERROR | MB_OK, szDomainAndUser);
break;
case STANDARD:
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
IDS_BADSTANDARDUSER, MB_ICONERROR | MB_OK, szDomainAndUser);
break;
case USEGROUPNAME:
default:
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION,
IDS_USR_CHANGEGROUP_ERR, MB_ICONERROR | MB_OK, szDomainAndUser, m_szGroups);
break;
}
break;
}
default:
{
TCHAR szMessage[512];
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD) status, 0, szMessage, ARRAYSIZE(szMessage), NULL))
LoadString(g_hinst, IDS_ERR_UNEXPECTED, szMessage, ARRAYSIZE(szMessage));
::DisplayFormatMessage(hwndError, IDS_USR_APPLET_CAPTION, IDS_ERR_ADDUSER, MB_ICONERROR | MB_OK, szMessage);
}
}
hr = E_FAIL;
}
}
return hr;
}
HRESULT CUserInfo::UpdateUsername(LPTSTR pszNewUsername)
{
CWaitCursor cur;
USER_INFO_0 usri0;
usri0.usri0_name = T2W(pszNewUsername);
DWORD dwErr;
NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 0, (BYTE*) &usri0, &dwErr);
if (status != NERR_Success)
return E_FAIL;
StrCpyN(m_szUsername, pszNewUsername, ARRAYSIZE(m_szUsername));
return S_OK;
}
HRESULT CUserInfo::UpdateFullName(LPTSTR pszFullName)
{
CWaitCursor cur;
USER_INFO_1011 usri1011;
usri1011.usri1011_full_name = T2W(pszFullName);
DWORD dwErr;
NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 1011, (BYTE*) &usri1011, &dwErr);
if (status != NERR_Success)
return E_FAIL;
StrCpyN(m_szFullName, pszFullName, ARRAYSIZE(m_szFullName));
return S_OK;
}
HRESULT CUserInfo::UpdatePassword(BOOL* pfBadPWFormat)
{
CWaitCursor cur;
RevealPassword();
USER_INFO_1003 usri1003;
usri1003.usri1003_password = T2W(m_szPasswordBuffer);
DWORD dwErr;
NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 1003, (BYTE*)&usri1003, &dwErr);
ZeroPassword(); // Kill the password
if (pfBadPWFormat != NULL)
*pfBadPWFormat = (status == NERR_PasswordTooShort);
return (status == NERR_Success) ? S_OK:E_FAIL;
}
HRESULT CUserInfo::UpdateGroup(HWND hwndError, LPTSTR pszGroup, GROUPPSEUDONYM grouppseudonym)
{
CWaitCursor cur;
// Save the old group before we change it
TCHAR szOldGroups[MAX_GROUP * 2 + 3];
StrCpyN(szOldGroups, m_szGroups, ARRAYSIZE(szOldGroups));
// Try to change the local group
StrCpyN(m_szGroups, pszGroup, ARRAYSIZE(m_szGroups));
HRESULT hr = ChangeLocalGroups(hwndError, grouppseudonym);
if (FAILED(hr))
StrCpyN(m_szGroups, szOldGroups, ARRAYSIZE(m_szGroups)); // Restore the old group in case of failure
return hr;
}
HRESULT CUserInfo::UpdateDescription(LPTSTR pszDescription)
{
CWaitCursor cur;
USER_INFO_1007 usri1007;
usri1007.usri1007_comment = T2W(pszDescription);
DWORD dwErr;
NET_API_STATUS status = NetUserSetInfo(NULL, T2W(m_szUsername), 1007, (BYTE*) &usri1007, &dwErr);
if (status != NERR_Success)
return E_FAIL;
StrCpyN(m_szComment, pszDescription, ARRAYSIZE(m_szComment));
return S_OK;
}
void CUserInfo::HidePassword()
{
m_Seed = 0;
RtlInitUnicodeString(&m_Password, m_szPasswordBuffer);
RtlRunEncodeUnicodeString(&m_Seed, &m_Password);
}
void CUserInfo::RevealPassword()
{
RtlRunDecodeUnicodeString(m_Seed, &m_Password);
}
void CUserInfo::ZeroPassword()
{
SecureZeroMemory(m_szPasswordBuffer, ARRAYSIZE(m_szPasswordBuffer));
}
/*******************************************************************
CUserListLoader implementation
*******************************************************************/
CUserListLoader::CUserListLoader()
{
m_hInitDoneEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
}
CUserListLoader::~CUserListLoader()
{
EndInitNow();
WaitForSingleObject(m_hInitDoneEvent, INFINITE);
}
BOOL CUserListLoader::HasUserBeenAdded(PSID psid)
{
// Walk the user list looking for a given username and domain
CUserInfo* pUserInfo = NULL;
BOOL fFound = FALSE;
for (int i = 0; i < m_dpaAddedUsers.GetPtrCount(); i ++)
{
pUserInfo = m_dpaAddedUsers.GetPtr(i);
if (pUserInfo->m_psid && psid && EqualSid(pUserInfo->m_psid, psid))
{
fFound = TRUE;
break;
}
}
return fFound;
}
HRESULT CUserListLoader::Initialize(HWND hwndUserListPage)
{
if (!m_hInitDoneEvent)
{
return E_FAIL;
}
// Tell any existing init thread to exit and wait for it to do so
m_fEndInitNow = TRUE;
WaitForSingleObject(m_hInitDoneEvent, INFINITE);
ResetEvent(m_hInitDoneEvent);
m_fEndInitNow = FALSE;
m_hwndUserListPage = hwndUserListPage;
// Launch the initialize thread
DWORD InitThreadId;
HANDLE hInitThread = CreateThread(NULL, 0, CUserListLoader::InitializeThread, (LPVOID) this, 0, &InitThreadId);
if (hInitThread == NULL)
return E_FAIL;
CloseHandle(hInitThread); // Let this thread go about his/her merry way
return S_OK;
}
HRESULT CUserListLoader::UpdateFromLocalGroup(LPWSTR szLocalGroup)
{
USES_CONVERSION;
DWORD_PTR dwResumeHandle = 0;
HRESULT hr = S_OK;
BOOL fBreakLoop = FALSE;
while(!fBreakLoop)
{
LOCALGROUP_MEMBERS_INFO_0* prgMembersInfo;
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
NET_API_STATUS status = NetLocalGroupGetMembers(NULL, szLocalGroup, 0, (BYTE**) &prgMembersInfo,
8192, &dwEntriesRead,
&dwTotalEntries, &dwResumeHandle);
if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
{
// for all the members in the structure, lets add them
DWORD iMember;
for (iMember = 0; ((iMember < dwEntriesRead) && (!m_fEndInitNow)); iMember ++)
{
hr = AddUserInformation(prgMembersInfo[iMember].lgrmi0_sid);
}
NetApiBufferFree((BYTE*) prgMembersInfo);
// See if we can avoid calling NetLocalGroupGetMembers again
fBreakLoop = ((dwEntriesRead == dwTotalEntries) || m_fEndInitNow);
}
else
{
fBreakLoop = TRUE;
hr = E_FAIL;
}
}
return hr;
}
HRESULT CUserListLoader::AddUserInformation(PSID psid)
{
// Only add this user if we haven't already
if (!HasUserBeenAdded(psid))
{
CUserInfo *pUserInfo = new CUserInfo;
if (!pUserInfo)
return E_OUTOFMEMORY;
if (SUCCEEDED(pUserInfo->Load(psid, FALSE)))
{
PostMessage(m_hwndUserListPage, WM_ADDUSERTOLIST, (WPARAM) FALSE, (LPARAM)pUserInfo);
m_dpaAddedUsers.AppendPtr(pUserInfo); // Remember we've added this user
}
}
return S_OK;
}
DWORD CUserListLoader::InitializeThread(LPVOID pvoid)
{
CUserListLoader *pthis = (CUserListLoader*)pvoid;
// First delete any old list
PostMessage(GetDlgItem(pthis->m_hwndUserListPage, IDC_USER_LIST), LVM_DELETEALLITEMS, 0, 0);
// Create a list of adready-added users so we don't add a user twice
// if they're in multiple local groups
if (pthis->m_dpaAddedUsers.Create(8))
{
// Read each local group
DWORD_PTR dwResumeHandle = 0;
BOOL fBreakLoop = FALSE;
while (!fBreakLoop)
{
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
LOCALGROUP_INFO_1* prgGroupInfo;
NET_API_STATUS status = NetLocalGroupEnum(NULL, 1, (BYTE**) &prgGroupInfo,
8192, &dwEntriesRead, &dwTotalEntries,
&dwResumeHandle);
if ((status == NERR_Success) || (status == ERROR_MORE_DATA))
{
// We got some local groups - add information for all users in these local groups to our list
DWORD iGroup;
for (iGroup = 0; ((iGroup < dwEntriesRead) && (!pthis->m_fEndInitNow)); iGroup ++)
{
pthis->UpdateFromLocalGroup(prgGroupInfo[iGroup].lgrpi1_name);
}
NetApiBufferFree((BYTE*) prgGroupInfo);
// Maybe we don't have to try NetLocalGroupEnum again (if we got all the groups)
fBreakLoop = ((dwEntriesRead == dwTotalEntries) || pthis->m_fEndInitNow);
}
else
{
fBreakLoop = TRUE;
}
}
// Its okay to orphan any CUserInfo pointers stored here; they'll be
// released when the ulistpg exits or reinits.
pthis->m_dpaAddedUsers.Destroy();
}
SetEvent(pthis->m_hInitDoneEvent);
SetCursor(LoadCursor(NULL, IDC_ARROW));
return 0;
}
// User utility functions
BOOL UserAlreadyHasPermission(CUserInfo* pUserInfo, HWND hwndMsgParent)
{
TCHAR szDomainUser[MAX_DOMAIN + MAX_USER + 2];
::MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
BOOL fHasPermission = FALSE;
// See if this user is already in local groups on this machine
DWORD dwEntriesRead, dwIgnore2;
LOCALGROUP_USERS_INFO_0* plgrui0 = NULL;
if (NERR_Success == NetUserGetLocalGroups(NULL, szDomainUser, 0, 0,
(LPBYTE*)&plgrui0, 8192,
&dwEntriesRead, &dwIgnore2))
{
fHasPermission = (0 != dwEntriesRead);
NetApiBufferFree((LPVOID) plgrui0);
}
if ((NULL != hwndMsgParent) && (fHasPermission))
{
// Display an error; the user doesn't have permission
TCHAR szDomainUser[MAX_DOMAIN + MAX_USER + 2];
MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername,
szDomainUser, ARRAYSIZE(szDomainUser));
DisplayFormatMessage(hwndMsgParent, IDS_USR_NEWUSERWIZARD_CAPTION,
IDS_USR_CREATE_USEREXISTS_ERROR, MB_OK | MB_ICONINFORMATION,
szDomainUser);
}
return fHasPermission;
}