1883 lines
54 KiB
C++
1883 lines
54 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1993 - 1999.
|
|
//
|
|
// File: User.cpp
|
|
//
|
|
// Contents: implementation of CLogonUser
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "priv.h"
|
|
|
|
#include "resource.h"
|
|
#include "UserOM.h"
|
|
#include <lmaccess.h> // for NetUserSetInfo & structures
|
|
#include <lmapibuf.h> // for NetApiBufferFree
|
|
#include <lmerr.h> // for NERR_Success
|
|
#include <ntlsa.h> // for LsaOpenPolicy, etc.
|
|
#include <sddl.h> // for ConvertSidToStringSid
|
|
#include <tchar.h> // for _TEOF
|
|
#include "LogonIPC.h"
|
|
#include "ProfileUtil.h"
|
|
#include <MSGinaExports.h>
|
|
#include <msshrui.h> // for IsFolderPrivateForUser, SetFolderPermissionsForSharing
|
|
#include <winsta.h> // for WinStationEnumerate, etc.
|
|
#include <ccstock.h>
|
|
|
|
#include <passrec.h> // PRQueryStatus, dpapi.lib
|
|
|
|
|
|
typedef struct
|
|
{
|
|
SID sid; // contains 1 subauthority
|
|
DWORD dwSubAuth; // 2nd subauthority
|
|
} _ALIAS_SID;
|
|
|
|
#define DECLARE_ALIAS_SID(rid) {{SID_REVISION,2,SECURITY_NT_AUTHORITY,{SECURITY_BUILTIN_DOMAIN_RID}},(rid)}
|
|
|
|
struct
|
|
{
|
|
_ALIAS_SID sid;
|
|
LPCWSTR szDefaultGroupName;
|
|
WCHAR szActualGroupName[GNLEN + sizeof('\0')];
|
|
} g_groupname_map [] =
|
|
{
|
|
// in ascending order of privilege
|
|
{ DECLARE_ALIAS_SID(DOMAIN_ALIAS_RID_GUESTS), L"Guests", L"" },
|
|
{ DECLARE_ALIAS_SID(DOMAIN_ALIAS_RID_USERS), L"Users", L"" },
|
|
{ DECLARE_ALIAS_SID(DOMAIN_ALIAS_RID_POWER_USERS), L"Power Users", L"" },
|
|
{ DECLARE_ALIAS_SID(DOMAIN_ALIAS_RID_ADMINS), L"Administrators", L"" }
|
|
};
|
|
|
|
void _InitializeGroupNames()
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAYSIZE(g_groupname_map); i++)
|
|
{
|
|
WCHAR szDomain[DNLEN + sizeof('\0')];
|
|
DWORD dwNameSize = ARRAYSIZE(g_groupname_map[i].szActualGroupName);
|
|
DWORD dwDomainSize = ARRAYSIZE(szDomain);
|
|
SID_NAME_USE eUse;
|
|
|
|
if (L'\0' == g_groupname_map[i].szActualGroupName[0] &&
|
|
!LookupAccountSidW(NULL,
|
|
&g_groupname_map[i].sid,
|
|
g_groupname_map[i].szActualGroupName,
|
|
&dwNameSize,
|
|
szDomain,
|
|
&dwDomainSize,
|
|
&eUse))
|
|
{
|
|
lstrcpynW(g_groupname_map[i].szActualGroupName,
|
|
g_groupname_map[i].szDefaultGroupName,
|
|
ARRAYSIZE(g_groupname_map[i].szActualGroupName));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// IUnknown Interface
|
|
//
|
|
|
|
ULONG CLogonUser::AddRef()
|
|
{
|
|
_cRef++;
|
|
return _cRef;
|
|
}
|
|
|
|
|
|
ULONG CLogonUser::Release()
|
|
{
|
|
ASSERT(_cRef > 0);
|
|
_cRef--;
|
|
|
|
if (_cRef > 0)
|
|
{
|
|
return _cRef;
|
|
}
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CLogonUser, IDispatch),
|
|
QITABENT(CLogonUser, ILogonUser),
|
|
{0},
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
|
|
//
|
|
// IDispatch Interface
|
|
//
|
|
|
|
STDMETHODIMP CLogonUser::GetTypeInfoCount(UINT* pctinfo)
|
|
{
|
|
return CIDispatchHelper::GetTypeInfoCount(pctinfo);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLogonUser::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
|
|
{
|
|
return CIDispatchHelper::GetTypeInfo(itinfo, lcid, pptinfo);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLogonUser::GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid)
|
|
{
|
|
return CIDispatchHelper::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLogonUser::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
|
|
{
|
|
return CIDispatchHelper::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
|
|
}
|
|
|
|
|
|
//
|
|
// ILogonUser Interface
|
|
//
|
|
STDMETHODIMP CLogonUser::get_setting(BSTR bstrName, VARIANT* pvarVal)
|
|
{
|
|
return _UserSettingAccessor(bstrName, pvarVal, FALSE);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLogonUser::put_setting(BSTR bstrName, VARIANT varVal)
|
|
{
|
|
return _UserSettingAccessor(bstrName, &varVal, TRUE);
|
|
}
|
|
|
|
STDMETHODIMP CLogonUser::get_isLoggedOn(VARIANT_BOOL* pbLoggedOn)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CLogonIPC objLogon;
|
|
|
|
if (NULL == pbLoggedOn)
|
|
return E_POINTER;
|
|
|
|
*pbLoggedOn = VARIANT_FALSE;
|
|
|
|
if (objLogon.IsLogonServiceAvailable())
|
|
{
|
|
*pbLoggedOn = ( objLogon.IsUserLoggedOn(_szLoginName, _szDomain) ) ? VARIANT_TRUE : VARIANT_FALSE;
|
|
}
|
|
else
|
|
{
|
|
TCHAR szUsername[UNLEN + sizeof('\0')];
|
|
DWORD cch = ARRAYSIZE(szUsername);
|
|
|
|
if (GetUserName(szUsername, &cch) && !StrCmp(szUsername, _szLoginName))
|
|
{
|
|
*pbLoggedOn = VARIANT_TRUE;
|
|
}
|
|
else
|
|
{
|
|
PLOGONID pSessions;
|
|
DWORD cSessions;
|
|
|
|
// Iterate the sessions looking for active and disconnected sessions only.
|
|
// Then match the user name and domain (case INsensitive) for a result.
|
|
|
|
if (WinStationEnumerate(SERVERNAME_CURRENT,
|
|
&pSessions,
|
|
&cSessions))
|
|
{
|
|
PLOGONID pSession;
|
|
DWORD i;
|
|
|
|
for (i = 0, pSession = pSessions; i < cSessions; ++i, ++pSession)
|
|
{
|
|
if ((pSession->State == State_Active) || (pSession->State == State_Disconnected))
|
|
{
|
|
WINSTATIONINFORMATION winStationInformation;
|
|
DWORD cb;
|
|
|
|
if (WinStationQueryInformation(SERVERNAME_CURRENT,
|
|
pSession->SessionId,
|
|
WinStationInformation,
|
|
&winStationInformation,
|
|
sizeof(winStationInformation),
|
|
&cb))
|
|
{
|
|
if ((0 == lstrcmpi(winStationInformation.UserName, _szLoginName)) &&
|
|
(0 == lstrcmpi(winStationInformation.Domain, _szDomain)))
|
|
{
|
|
*pbLoggedOn = VARIANT_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
WinStationFreeMemory(pSessions);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
dwErrorCode = GetLastError();
|
|
|
|
// We get RPC_S_INVALID_BINDING in safe mode, in which case
|
|
// FUS is disabled, so we know the user isn't logged on.
|
|
if (dwErrorCode != RPC_S_INVALID_BINDING)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwErrorCode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLogonUser::get_passwordRequired(VARIANT_BOOL* pbPasswordRequired)
|
|
{
|
|
CLogonIPC objLogon;
|
|
|
|
if (objLogon.IsLogonServiceAvailable())
|
|
{
|
|
*pbPasswordRequired = objLogon.TestBlankPassword(_szLoginName, _szDomain) ? VARIANT_FALSE: VARIANT_TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (NULL == pbPasswordRequired)
|
|
return E_POINTER;
|
|
|
|
if ((BOOL)-1 == _bPasswordRequired)
|
|
{
|
|
BOOL fResult;
|
|
HANDLE hToken;
|
|
|
|
// Test for a blank password by trying to
|
|
// logon the user with a blank password.
|
|
|
|
fResult = LogonUser(_szLoginName,
|
|
NULL,
|
|
L"",
|
|
LOGON32_LOGON_INTERACTIVE,
|
|
LOGON32_PROVIDER_DEFAULT,
|
|
&hToken);
|
|
if (fResult != FALSE)
|
|
{
|
|
TBOOL(CloseHandle(hToken));
|
|
_bPasswordRequired = FALSE;
|
|
}
|
|
else
|
|
{
|
|
switch (GetLastError())
|
|
{
|
|
case ERROR_ACCOUNT_RESTRICTION:
|
|
// This means that blank password logons are disallowed, from
|
|
// which we infer that the password is blank.
|
|
_bPasswordRequired = FALSE;
|
|
break;
|
|
|
|
case ERROR_LOGON_TYPE_NOT_GRANTED:
|
|
// Interactive logon was denied. We only get this if the
|
|
// password is blank, otherwise we get ERROR_LOGON_FAILURE.
|
|
_bPasswordRequired = FALSE;
|
|
break;
|
|
|
|
case ERROR_LOGON_FAILURE: // normal case (non-blank password)
|
|
case ERROR_PASSWORD_MUST_CHANGE: // expired password
|
|
_bPasswordRequired = TRUE;
|
|
break;
|
|
|
|
default:
|
|
// We'll guess TRUE
|
|
_bPasswordRequired = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
*pbPasswordRequired = (FALSE != _bPasswordRequired) ? VARIANT_TRUE : VARIANT_FALSE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CLogonUser::get_interactiveLogonAllowed(VARIANT_BOOL *pbInteractiveLogonAllowed)
|
|
{
|
|
HRESULT hr;
|
|
CLogonIPC objLogon;
|
|
|
|
if (objLogon.IsLogonServiceAvailable())
|
|
{
|
|
*pbInteractiveLogonAllowed = objLogon.TestInteractiveLogonAllowed(_szLoginName, _szDomain) ? VARIANT_TRUE : VARIANT_FALSE;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
int iResult;
|
|
|
|
iResult = ShellIsUserInteractiveLogonAllowed(_szLoginName);
|
|
if (iResult == -1)
|
|
{
|
|
hr = E_ACCESSDENIED;
|
|
}
|
|
else
|
|
{
|
|
*pbInteractiveLogonAllowed = (iResult != 0) ? VARIANT_TRUE : VARIANT_FALSE;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT _IsGuestAccessMode(void)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (IsOS(OS_PERSONAL))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else if (IsOS(OS_PROFESSIONAL) && !IsOS(OS_DOMAINMEMBER))
|
|
{
|
|
DWORD dwValue = 0;
|
|
DWORD cbValue = sizeof(dwValue);
|
|
if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
|
|
TEXT("SYSTEM\\CurrentControlSet\\Control\\LSA"),
|
|
TEXT("ForceGuest"),
|
|
NULL,
|
|
&dwValue,
|
|
&cbValue)
|
|
&& 1 == dwValue)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HMODULE g_hmodNTShrUI = NULL;
|
|
static PFNISFOLDERPRIVATEFORUSER g_pfnIsFolderPrivateForUser = NULL;
|
|
static PFNSETFOLDERPERMISSIONSFORSHARING g_pfnSetFolderPermissionsForSharing = NULL;
|
|
|
|
void _LoadNTShrUI(void)
|
|
{
|
|
if (NULL == g_hmodNTShrUI)
|
|
{
|
|
g_hmodNTShrUI = LoadLibraryW(L"ntshrui.dll");
|
|
|
|
if (NULL != g_hmodNTShrUI)
|
|
{
|
|
g_pfnIsFolderPrivateForUser = (PFNISFOLDERPRIVATEFORUSER)GetProcAddress(g_hmodNTShrUI, "IsFolderPrivateForUser");
|
|
g_pfnSetFolderPermissionsForSharing = (PFNSETFOLDERPERMISSIONSFORSHARING)GetProcAddress(g_hmodNTShrUI, "SetFolderPermissionsForSharing");
|
|
}
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CLogonUser::get_isProfilePrivate(VARIANT_BOOL* pbPrivate)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (NULL == pbPrivate)
|
|
return E_POINTER;
|
|
|
|
*pbPrivate = VARIANT_FALSE;
|
|
|
|
// Only succeed if we are on Personal, or Professional with ForceGuest=1.
|
|
hr = _IsGuestAccessMode();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// assume failure here
|
|
hr = E_FAIL;
|
|
|
|
_LookupUserSid();
|
|
if (NULL != _pszSID)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
// Get the profile path
|
|
PathCombine(szPath, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"), _pszSID);
|
|
DWORD cbData = sizeof(szPath);
|
|
if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
|
|
szPath,
|
|
TEXT("ProfileImagePath"),
|
|
NULL,
|
|
szPath,
|
|
&cbData))
|
|
{
|
|
DWORD dwPrivateType;
|
|
|
|
_LoadNTShrUI();
|
|
|
|
if (NULL != g_pfnIsFolderPrivateForUser &&
|
|
g_pfnIsFolderPrivateForUser(szPath, _pszSID, &dwPrivateType, NULL))
|
|
{
|
|
// Note that we return E_FAIL for FAT volumes
|
|
if (0 == (dwPrivateType & IFPFU_NOT_NTFS))
|
|
{
|
|
if (dwPrivateType & IFPFU_PRIVATE)
|
|
{
|
|
*pbPrivate = VARIANT_TRUE;
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CLogonUser::makeProfilePrivate(VARIANT_BOOL bPrivate)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Only succeed if we are on Personal, or Professional with ForceGuest=1.
|
|
hr = _IsGuestAccessMode();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// assume failure here
|
|
hr = E_FAIL;
|
|
|
|
_LookupUserSid();
|
|
if (NULL != _pszSID)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
|
|
// Get the profile path
|
|
PathCombine(szPath, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"), _pszSID);
|
|
DWORD cbData = sizeof(szPath);
|
|
if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
|
|
szPath,
|
|
TEXT("ProfileImagePath"),
|
|
NULL,
|
|
szPath,
|
|
&cbData))
|
|
{
|
|
_LoadNTShrUI();
|
|
|
|
if (NULL != g_pfnSetFolderPermissionsForSharing &&
|
|
g_pfnSetFolderPermissionsForSharing(szPath, _pszSID, (VARIANT_TRUE == bPrivate) ? 0 : 1, NULL))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CLogonUser::logon(BSTR pbstrPassword, VARIANT_BOOL* pbRet)
|
|
{
|
|
HRESULT hr;
|
|
CLogonIPC objLogon;
|
|
TCHAR szPassword[PWLEN + sizeof('\0')];
|
|
|
|
if (pbstrPassword)
|
|
lstrcpyn(szPassword, pbstrPassword, ARRAYSIZE(szPassword));
|
|
else
|
|
szPassword[0] = 0;
|
|
|
|
if (!objLogon.IsLogonServiceAvailable())
|
|
{
|
|
*pbRet = VARIANT_FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
if (objLogon.LogUserOn (_szLoginName, _szDomain, szPassword))
|
|
*pbRet = VARIANT_TRUE;
|
|
else
|
|
*pbRet = VARIANT_FALSE;
|
|
|
|
|
|
if (*pbRet)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLogonUser::logoff(VARIANT_BOOL* pbRet)
|
|
{
|
|
HRESULT hr;
|
|
CLogonIPC objLogon;
|
|
|
|
if (objLogon.IsLogonServiceAvailable())
|
|
{
|
|
*pbRet = ( objLogon.LogUserOff(_szLoginName, _szDomain) ) ? VARIANT_TRUE : VARIANT_FALSE;
|
|
}
|
|
else
|
|
{
|
|
*pbRet = ( ExitWindowsEx(EWX_LOGOFF, 0) ) ? VARIANT_TRUE : VARIANT_FALSE;
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Borrowed from msgina
|
|
BOOL IsAutologonUser(LPCTSTR szUser, LPCTSTR szDomain)
|
|
{
|
|
BOOL fIsUser = FALSE;
|
|
HKEY hkey = NULL;
|
|
TCHAR szAutologonUser[UNLEN + sizeof('\0')];
|
|
TCHAR szAutologonDomain[DNLEN + sizeof('\0')];
|
|
TCHAR szTempDomainBuffer[DNLEN + sizeof('\0')];
|
|
DWORD cbBuffer;
|
|
DWORD dwType;
|
|
|
|
*szTempDomainBuffer = 0;
|
|
|
|
// Domain may be a empty string. If this is the case...
|
|
if (0 == *szDomain)
|
|
{
|
|
DWORD cchBuffer;
|
|
|
|
// We really mean the local machine name
|
|
// Point to our local buffer
|
|
szDomain = szTempDomainBuffer;
|
|
cchBuffer = ARRAYSIZE(szTempDomainBuffer);
|
|
|
|
GetComputerName(szTempDomainBuffer, &cchBuffer);
|
|
}
|
|
|
|
// See if the domain and user name
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon"),
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hkey))
|
|
{
|
|
// Check the user name
|
|
cbBuffer = sizeof (szAutologonUser);
|
|
if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("DefaultUserName"), 0, &dwType, (LPBYTE)szAutologonUser, &cbBuffer))
|
|
{
|
|
// Does it match?
|
|
if (0 == lstrcmpi(szAutologonUser, szUser))
|
|
{
|
|
// Yes. Now check domain
|
|
cbBuffer = sizeof(szAutologonDomain);
|
|
if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("DefaultDomainName"), 0, &dwType, (LPBYTE)szAutologonDomain, &cbBuffer))
|
|
{
|
|
// Make sure domain matches
|
|
if (0 == lstrcmpi(szAutologonDomain, szDomain))
|
|
{
|
|
// Success - the users match
|
|
fIsUser = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
return fIsUser;
|
|
}
|
|
|
|
// Borrowed from msgina
|
|
NTSTATUS SetAutologonPassword(LPCWSTR szPassword)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
LSA_HANDLE LsaHandle = NULL;
|
|
UNICODE_STRING SecretName;
|
|
UNICODE_STRING SecretValue;
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes, NULL, 0L, (HANDLE)NULL, NULL);
|
|
|
|
Status = LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_CREATE_SECRET, &LsaHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
RtlInitUnicodeString(&SecretName, L"DefaultPassword");
|
|
RtlInitUnicodeString(&SecretValue, szPassword);
|
|
|
|
Status = LsaStorePrivateData(LsaHandle, &SecretName, &SecretValue);
|
|
LsaClose(LsaHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
STDMETHODIMP CLogonUser::changePassword(VARIANT varNewPassword, VARIANT varOldPassword, VARIANT_BOOL* pbRet)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (VT_BSTR == varNewPassword.vt && VT_BSTR == varOldPassword.vt)
|
|
{
|
|
TCHAR szUsername[UNLEN + sizeof('\0')];
|
|
DWORD cch = ARRAYSIZE(szUsername);
|
|
NET_API_STATUS nasRet;
|
|
USER_MODALS_INFO_0 *pumi0 = NULL;
|
|
|
|
LPWSTR pszNewPassword = varNewPassword.bstrVal ? varNewPassword.bstrVal : L"\0";
|
|
|
|
// We used to create accounts with UF_PASSWD_NOTREQD, and we still do
|
|
// when password policy is enabled. If UF_PASSWD_NOTREQD is set, then
|
|
// the below code will succeed even with password policy is enabled,
|
|
// so do a minimal policy check here.
|
|
|
|
nasRet = NetUserModalsGet(NULL, 0, (LPBYTE*)&pumi0);
|
|
if (nasRet == NERR_Success && pumi0 != NULL)
|
|
{
|
|
if ((DWORD)lstrlen(pszNewPassword) < pumi0->usrmod0_min_passwd_len)
|
|
{
|
|
nasRet = NERR_PasswordTooShort;
|
|
}
|
|
NetApiBufferFree(pumi0);
|
|
}
|
|
|
|
if (nasRet == NERR_Success)
|
|
{
|
|
if (GetUserName(szUsername, &cch) && !StrCmp(szUsername, _szLoginName))
|
|
{
|
|
// This is the case of a user changing their own password.
|
|
// Both passwords must be provided to effect the change.
|
|
|
|
LPCWSTR pszOldPassword = varOldPassword.bstrVal ? varOldPassword.bstrVal : L"\0";
|
|
|
|
nasRet = NetUserChangePassword(NULL, // Local machine
|
|
_szLoginName, // name of the person to change
|
|
pszOldPassword, // old password
|
|
pszNewPassword); // new password
|
|
}
|
|
else
|
|
{
|
|
// This is the case of an admin changing someone else's password.
|
|
// As an administrator they don't need to enter the old password.
|
|
|
|
USER_INFO_1003 usri1003 = { pszNewPassword };
|
|
|
|
nasRet = NetUserSetInfo(NULL, // local machine
|
|
_szLoginName, // name of the person to change
|
|
1003, // structure level
|
|
(LPBYTE)&usri1003, // the update info
|
|
NULL); // don't care
|
|
}
|
|
|
|
if (nasRet == NERR_Success)
|
|
{
|
|
// If this is the default user for autologon, delete the cleartext
|
|
// password from the registry and save the new password.
|
|
|
|
if (IsAutologonUser(_szLoginName, _szDomain))
|
|
{
|
|
SHDeleteValue(HKEY_LOCAL_MACHINE,
|
|
TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon"),
|
|
TEXT("DefaultPassword"));
|
|
SetAutologonPassword(pszNewPassword);
|
|
}
|
|
|
|
// Make an attempt to remove UF_PASSWD_NOTREQD if it's
|
|
// currently set. Ignore errors since we already changed
|
|
// the password above.
|
|
|
|
USER_INFO_1008 *pusri1008;
|
|
if (NERR_Success == NetUserGetInfo(NULL, _szLoginName, 1008, (LPBYTE*)&pusri1008))
|
|
{
|
|
if (pusri1008->usri1008_flags & UF_PASSWD_NOTREQD)
|
|
{
|
|
pusri1008->usri1008_flags &= ~UF_PASSWD_NOTREQD;
|
|
NetUserSetInfo(NULL, _szLoginName, 1008, (LPBYTE)pusri1008, NULL);
|
|
}
|
|
NetApiBufferFree(pusri1008);
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = HRESULT_FROM_WIN32(nasRet);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_bPasswordRequired = !(L'\0' == *pszNewPassword);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
*pbRet = ( SUCCEEDED(hr) ) ? VARIANT_TRUE : VARIANT_FALSE;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDAPI CLogonUser_Create(REFIID riid, void** ppvObj)
|
|
{
|
|
return CLogonUser::Create(TEXT(""), TEXT(""), TEXT(""), riid, ppvObj);
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::Create(LPCTSTR pszLoginName, LPCTSTR pszFullName, LPCTSTR pszDomain, REFIID riid, LPVOID* ppv)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
CLogonUser* pUser = new CLogonUser(pszLoginName, pszFullName, pszDomain);
|
|
|
|
if (pUser)
|
|
{
|
|
hr = pUser->QueryInterface(riid, ppv);
|
|
pUser->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
CLogonUser::CLogonUser(LPCTSTR pszLoginName,
|
|
LPCTSTR pszFullName,
|
|
LPCTSTR pszDomain)
|
|
: _cRef(1), CIDispatchHelper(&IID_ILogonUser, &LIBID_SHGINALib),
|
|
_strDisplayName(NULL), _strPictureSource(NULL), _strDescription(NULL),
|
|
_strHint(NULL), _iPrivilegeLevel(-1), _pszSID(NULL),
|
|
_bPasswordRequired((BOOL)-1)
|
|
{
|
|
_InitializeGroupNames();
|
|
|
|
lstrcpyn(_szLoginName, pszLoginName, ARRAYSIZE(_szLoginName));
|
|
lstrcpyn(_szDomain, pszDomain, ARRAYSIZE(_szDomain));
|
|
|
|
if (pszFullName)
|
|
_strDisplayName = SysAllocString(pszFullName);
|
|
|
|
// Use the EOF marker to indicate an uninitialized string
|
|
_szPicture[0] = _TEOF;
|
|
|
|
DllAddRef();
|
|
}
|
|
|
|
|
|
CLogonUser::~CLogonUser()
|
|
{
|
|
SysFreeString(_strDisplayName);
|
|
SysFreeString(_strPictureSource);
|
|
SysFreeString(_strDescription);
|
|
SysFreeString(_strHint);
|
|
|
|
if (_pszSID) LocalFree(_pszSID);
|
|
|
|
ASSERT(_cRef == 0);
|
|
DllRelease();
|
|
}
|
|
|
|
|
|
typedef HRESULT (CLogonUser::*PFNPUT)(VARIANT);
|
|
typedef HRESULT (CLogonUser::*PFNGET)(VARIANT *);
|
|
|
|
struct SETTINGMAP
|
|
{
|
|
LPCWSTR szSetting;
|
|
PFNGET pfnGet;
|
|
PFNPUT pfnPut;
|
|
};
|
|
|
|
#define MAP_SETTING(x) { L#x, CLogonUser::_Get##x, CLogonUser::_Put##x }
|
|
#define MAP_SETTING_GET_ONLY(x) { L#x, CLogonUser::_Get##x, NULL }
|
|
#define MAP_SETTING_PUT_ONLY(x) { L#x, NULL, CLogonUser::_Put##x }
|
|
|
|
// _UserSettingAccessor
|
|
//
|
|
// bstrName - name of the setting you widh to access
|
|
// pvarVal - the value of the named setting
|
|
// bPut - if true the named setting will be updated
|
|
// with the value pointed to by pvarVal
|
|
// if false the named setting will be retrieved
|
|
// in pvarVal
|
|
//
|
|
HRESULT CLogonUser::_UserSettingAccessor(BSTR bstrName, VARIANT *pvarVal, BOOL bPut)
|
|
{
|
|
static const SETTINGMAP setting_map[] =
|
|
{
|
|
// in descending order of expected access frequecy
|
|
MAP_SETTING(LoginName),
|
|
MAP_SETTING(DisplayName),
|
|
MAP_SETTING(Picture),
|
|
MAP_SETTING_GET_ONLY(PictureSource),
|
|
MAP_SETTING(AccountType),
|
|
MAP_SETTING(Hint),
|
|
MAP_SETTING_GET_ONLY(Domain),
|
|
MAP_SETTING(Description),
|
|
MAP_SETTING_GET_ONLY(SID),
|
|
MAP_SETTING_GET_ONLY(UnreadMail)
|
|
};
|
|
|
|
HRESULT hr;
|
|
INT i;
|
|
|
|
// start off assuming bogus setting name
|
|
hr = E_INVALIDARG;
|
|
|
|
for ( i = 0; i < ARRAYSIZE(setting_map); i++)
|
|
{
|
|
if ( StrCmpW(bstrName, setting_map[i].szSetting) == 0 )
|
|
{
|
|
|
|
// what do we want to do with the named setting ...
|
|
if ( bPut )
|
|
{
|
|
// ... change its value
|
|
PFNPUT pfnPut = setting_map[i].pfnPut;
|
|
|
|
if ( pfnPut != NULL )
|
|
{
|
|
hr = (this->*pfnPut)(*pvarVal);
|
|
}
|
|
else
|
|
{
|
|
// we don't support updated the value for this setting
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ... retrieve its value
|
|
PFNGET pfnGet = setting_map[i].pfnGet;
|
|
|
|
if ( pfnGet != NULL )
|
|
{
|
|
hr = (this->*pfnGet)(pvarVal);
|
|
}
|
|
else
|
|
{
|
|
// we don't support retieving the value for this setting
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CLogonUser::_GetDisplayName(VARIANT* pvar)
|
|
{
|
|
if (NULL == pvar)
|
|
return E_POINTER;
|
|
|
|
if (NULL == _strDisplayName)
|
|
{
|
|
PUSER_INFO_1011 pusri1011 = NULL;
|
|
NET_API_STATUS nasRet;
|
|
|
|
nasRet = NetUserGetInfo(NULL, // local machine
|
|
_szLoginName, // whose information do we want
|
|
1011, // structure level
|
|
(LPBYTE*)&pusri1011); // pointer to the structure we'll receive
|
|
|
|
if ( nasRet == NERR_Success )
|
|
{
|
|
_strDisplayName = SysAllocString(pusri1011->usri1011_full_name);
|
|
NetApiBufferFree(pusri1011);
|
|
}
|
|
}
|
|
|
|
pvar->vt = VT_BSTR;
|
|
pvar->bstrVal = SysAllocString(_strDisplayName);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_PutDisplayName(VARIANT var)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ( var.vt == VT_BSTR )
|
|
{
|
|
USER_INFO_1011 usri1011;
|
|
NET_API_STATUS nasRet;
|
|
|
|
if ( var.bstrVal )
|
|
{
|
|
usri1011.usri1011_full_name = var.bstrVal;
|
|
}
|
|
else
|
|
{
|
|
// OK to have emply string as display name
|
|
usri1011.usri1011_full_name = L"\0";
|
|
}
|
|
|
|
nasRet = NetUserSetInfo(NULL, // local machine
|
|
_szLoginName, // name of the person to change
|
|
1011, // structure level
|
|
(LPBYTE)&usri1011, // the update info
|
|
NULL); // don't care
|
|
|
|
if ( nasRet == NERR_Success )
|
|
{
|
|
// DisplayName was successfully changed. Remember to update our
|
|
// local copy
|
|
SysFreeString(_strDisplayName);
|
|
_strDisplayName = SysAllocString(usri1011.usri1011_full_name);
|
|
|
|
// Notify everyone that a user name has changed
|
|
SHChangeDWORDAsIDList dwidl;
|
|
dwidl.cb = SIZEOF(dwidl) - SIZEOF(dwidl.cbZero);
|
|
dwidl.dwItem1 = SHCNEE_USERINFOCHANGED;
|
|
dwidl.dwItem2 = 0;
|
|
dwidl.cbZero = 0;
|
|
SHChangeNotify(SHCNE_EXTENDED_EVENT, SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, (LPCITEMIDLIST)&dwidl, NULL);
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
// insufficient privileges?
|
|
hr = HRESULT_FROM_WIN32(nasRet);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_GetLoginName(VARIANT* pvar)
|
|
{
|
|
if (NULL == pvar)
|
|
return E_POINTER;
|
|
|
|
pvar->vt = VT_BSTR;
|
|
pvar->bstrVal = SysAllocString(_szLoginName);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_PutLoginName(VARIANT var)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ( (var.vt == VT_BSTR) && (var.bstrVal) && (*var.bstrVal) )
|
|
{
|
|
if (_szLoginName[0] == TEXT('\0'))
|
|
{
|
|
// We haven't been initialized yet. Initialize to the given name.
|
|
lstrcpyn(_szLoginName, var.bstrVal, ARRAYSIZE(_szLoginName));
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
USER_INFO_0 usri0;
|
|
NET_API_STATUS nasRet;
|
|
|
|
usri0.usri0_name = var.bstrVal;
|
|
nasRet = NetUserSetInfo(NULL, // local machine
|
|
_szLoginName, // name of the person to change
|
|
0, // structure level
|
|
(LPBYTE)&usri0, // the update info
|
|
NULL); // don't care
|
|
|
|
if ( nasRet == NERR_Success )
|
|
{
|
|
// We should also rename the user's picture file to match
|
|
// their new LoginName
|
|
if (_TEOF == _szPicture[0])
|
|
{
|
|
// This requires _szLoginName to still have the old name,
|
|
// so do it before updating _szLoginName below.
|
|
_InitPicture();
|
|
}
|
|
|
|
if (TEXT('\0') != _szPicture[0])
|
|
{
|
|
TCHAR szNewPicture[ARRAYSIZE(_szPicture)];
|
|
LPTSTR szFileName;
|
|
LPTSTR szFileExt;
|
|
|
|
szFileName = PathFindFileName(&_szPicture[7]);
|
|
szFileExt = PathFindExtension(szFileName);
|
|
|
|
lstrcpyn(szNewPicture, _szPicture, (int)((szFileName - _szPicture) + 1));
|
|
lstrcatn(szNewPicture, usri0.usri0_name, ARRAYSIZE(szNewPicture));
|
|
lstrcatn(szNewPicture, szFileExt, ARRAYSIZE(szNewPicture));
|
|
|
|
if ( MoveFileEx(&_szPicture[7], &szNewPicture[7], MOVEFILE_REPLACE_EXISTING) )
|
|
{
|
|
lstrcpyn(_szPicture, szNewPicture, ARRAYSIZE(_szPicture));
|
|
}
|
|
else
|
|
{
|
|
// Give up and just try to delete the old picture
|
|
// (otherwise it will be abandoned).
|
|
DeleteFile(&_szPicture[7]);
|
|
_szPicture[0] = _TEOF;
|
|
}
|
|
}
|
|
|
|
// LoginName was successfully changed. Remember to update our
|
|
// local copy
|
|
lstrcpyn(_szLoginName, usri0.usri0_name, ARRAYSIZE(_szLoginName));
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
// insufficient privileges?
|
|
hr = HRESULT_FROM_WIN32(nasRet);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_GetDomain(VARIANT* pvar)
|
|
{
|
|
if (NULL == pvar)
|
|
return E_POINTER;
|
|
|
|
pvar->vt = VT_BSTR;
|
|
pvar->bstrVal = SysAllocString(_szDomain);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_GetPicture(VARIANT* pvar)
|
|
{
|
|
if (NULL == pvar)
|
|
return E_POINTER;
|
|
|
|
if (_TEOF == _szPicture[0])
|
|
{
|
|
_InitPicture();
|
|
}
|
|
|
|
pvar->vt = VT_BSTR;
|
|
pvar->bstrVal = SysAllocString(_szPicture);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_PutPicture(VARIANT var)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ( (var.vt == VT_BSTR) && (var.bstrVal) && (*var.bstrVal) )
|
|
{
|
|
// Passed a string which is not NULL and not empty
|
|
|
|
TCHAR szNewPicturePath[MAX_PATH];
|
|
DWORD dwSize = ARRAYSIZE(szNewPicturePath);
|
|
|
|
// get the path of the image we want to copy
|
|
if ( PathIsURL(var.bstrVal) )
|
|
{
|
|
PathCreateFromUrl(var.bstrVal, szNewPicturePath, &dwSize, NULL);
|
|
}
|
|
else
|
|
{
|
|
lstrcpyn(szNewPicturePath, var.bstrVal, ARRAYSIZE(szNewPicturePath));
|
|
}
|
|
|
|
// REVIEW (phellar) : we build the URL string ourself so we know it's of the form,
|
|
// file://<path>, the path starts on the 7th character.
|
|
|
|
if ( _TEOF == _szPicture[0] || StrCmpI(szNewPicturePath, &_szPicture[7]) != 0 )
|
|
{
|
|
hr = _SetPicture(szNewPicturePath);
|
|
}
|
|
else
|
|
{
|
|
// Src and Dest paths are the same
|
|
|
|
// nothing to do
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CLogonUser::_GetPictureSource(VARIANT* pvar)
|
|
{
|
|
if (NULL == pvar)
|
|
return E_POINTER;
|
|
|
|
if (NULL == _strPictureSource)
|
|
{
|
|
TCHAR szHintKey[MAX_PATH];
|
|
DWORD dwType = REG_SZ;
|
|
DWORD dwSize = 0;
|
|
|
|
PathCombine(szHintKey, c_szRegRoot, _szLoginName);
|
|
if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
|
|
szHintKey,
|
|
c_szPictureSrcVal,
|
|
&dwType,
|
|
NULL,
|
|
&dwSize)
|
|
&& REG_SZ == dwType && dwSize > 0)
|
|
{
|
|
_strPictureSource = SysAllocStringLen(NULL, dwSize/sizeof(TCHAR));
|
|
if (NULL != _strPictureSource)
|
|
{
|
|
if (ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE,
|
|
szHintKey,
|
|
c_szPictureSrcVal,
|
|
NULL,
|
|
(LPVOID)_strPictureSource,
|
|
&dwSize))
|
|
{
|
|
SysFreeString(_strPictureSource);
|
|
_strPictureSource = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pvar->vt = VT_BSTR;
|
|
pvar->bstrVal = SysAllocString(_strPictureSource);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CLogonUser::_GetDescription(VARIANT* pvar)
|
|
{
|
|
if (NULL == pvar)
|
|
return E_POINTER;
|
|
|
|
if (NULL == _strDescription)
|
|
{
|
|
NET_API_STATUS nasRet;
|
|
USER_INFO_1007 *pusri1007;
|
|
|
|
nasRet = NetUserGetInfo(NULL, // local machine
|
|
_szLoginName, // whose information do we want
|
|
1007, // structure level
|
|
(LPBYTE*)&pusri1007); // pointer to the structure we'll receive
|
|
|
|
if ( nasRet == NERR_Success )
|
|
{
|
|
_strDescription = SysAllocString(pusri1007->usri1007_comment);
|
|
NetApiBufferFree(pusri1007);
|
|
}
|
|
}
|
|
|
|
pvar->vt = VT_BSTR;
|
|
pvar->bstrVal = SysAllocString(_strDescription);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_PutDescription(VARIANT var)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ( var.vt == VT_BSTR )
|
|
{
|
|
USER_INFO_1007 usri1007;
|
|
NET_API_STATUS nasRet;
|
|
|
|
if ( var.bstrVal )
|
|
{
|
|
usri1007.usri1007_comment = var.bstrVal;
|
|
}
|
|
else
|
|
{
|
|
// OK to have emply string as a description
|
|
usri1007.usri1007_comment = L"\0";
|
|
}
|
|
|
|
nasRet = NetUserSetInfo(NULL, // local machine
|
|
_szLoginName, // name of the person to change
|
|
1007, // structure level
|
|
(LPBYTE)&usri1007, // the update info
|
|
NULL); // don't care
|
|
|
|
if ( nasRet == NERR_Success )
|
|
{
|
|
// Description was successfully changed. Remember to update our
|
|
// local copy
|
|
SysFreeString(_strDescription);
|
|
_strDescription = SysAllocString(usri1007.usri1007_comment);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
// insufficient privileges?
|
|
hr = HRESULT_FROM_WIN32(nasRet);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_GetHint(VARIANT* pvar)
|
|
{
|
|
if (NULL == pvar)
|
|
return E_POINTER;
|
|
|
|
if (NULL == _strHint)
|
|
{
|
|
TCHAR szHintKey[MAX_PATH];
|
|
DWORD dwType = REG_SZ;
|
|
DWORD dwSize = 0;
|
|
|
|
PathCombine(szHintKey, c_szRegRoot, _szLoginName);
|
|
if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
|
|
szHintKey,
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&dwSize)
|
|
&& REG_SZ == dwType && dwSize > 0 && dwSize < 512)
|
|
{
|
|
_strHint = SysAllocStringLen(NULL, dwSize/sizeof(TCHAR));
|
|
if (NULL != _strHint)
|
|
{
|
|
if (ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE,
|
|
szHintKey,
|
|
NULL,
|
|
NULL,
|
|
(LPVOID)_strHint,
|
|
&dwSize))
|
|
{
|
|
SysFreeString(_strHint);
|
|
_strHint = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pvar->vt = VT_BSTR;
|
|
pvar->bstrVal = SysAllocString(_strHint);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_PutHint(VARIANT var)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ( var.vt == VT_BSTR )
|
|
{
|
|
DWORD dwErr;
|
|
TCHAR *pszHint;
|
|
HKEY hkUserHint;
|
|
|
|
if (var.bstrVal)
|
|
{
|
|
pszHint = var.bstrVal;
|
|
}
|
|
else
|
|
{
|
|
pszHint = TEXT("\0");
|
|
}
|
|
|
|
dwErr = _OpenUserHintKey(KEY_SET_VALUE, &hkUserHint);
|
|
|
|
if ( dwErr == ERROR_SUCCESS )
|
|
{
|
|
DWORD cbData = lstrlen(pszHint) * sizeof(TCHAR) + sizeof(TEXT('\0'));
|
|
dwErr = RegSetValueEx(hkUserHint,
|
|
NULL,
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE)pszHint,
|
|
cbData);
|
|
RegCloseKey(hkUserHint);
|
|
}
|
|
|
|
if ( dwErr == ERROR_SUCCESS )
|
|
{
|
|
// Hint was successfully changed. Remember to update our local copy
|
|
SysFreeString(_strHint);
|
|
_strHint = SysAllocString(pszHint);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CLogonUser::_GetAccountType(VARIANT* pvar)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = E_FAIL;
|
|
|
|
if (pvar)
|
|
{
|
|
if (-1 == _iPrivilegeLevel)
|
|
{
|
|
NET_API_STATUS nasRet;
|
|
PLOCALGROUP_INFO_0 plgi0;
|
|
DWORD dwEntriesRead;
|
|
DWORD dwEntriesTotal;
|
|
|
|
nasRet = NetUserGetLocalGroups(
|
|
NULL,
|
|
_szLoginName,
|
|
0,
|
|
0,
|
|
(LPBYTE *)&plgi0,
|
|
MAX_PREFERRED_LENGTH,
|
|
&dwEntriesRead,
|
|
&dwEntriesTotal);
|
|
|
|
if ( nasRet == NERR_Success )
|
|
{
|
|
// make sure we read all the groups
|
|
ASSERT(dwEntriesRead == dwEntriesTotal)
|
|
|
|
INT i, j, iMostPrivileged;
|
|
|
|
for (i = 0, iMostPrivileged = 0; i < (INT)dwEntriesRead; i++)
|
|
{
|
|
for (j = ARRAYSIZE(g_groupname_map)-1; j > 0; j--)
|
|
{
|
|
if ( lstrcmpiW(plgi0[i].lgrpi0_name, g_groupname_map[j].szActualGroupName) == 0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
iMostPrivileged = (iMostPrivileged > j) ? iMostPrivileged : j;
|
|
}
|
|
|
|
_iPrivilegeLevel = iMostPrivileged;
|
|
|
|
nasRet = NetApiBufferFree((LPVOID)plgi0);
|
|
}
|
|
hr = HRESULT_FROM_WIN32(nasRet);
|
|
}
|
|
|
|
if (-1 != _iPrivilegeLevel)
|
|
{
|
|
pvar->vt = VT_I4;
|
|
pvar->lVal = _iPrivilegeLevel;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_PutAccountType(VARIANT var)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = VariantChangeType(&var, &var, 0, VT_I4);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (var.lVal < 0 || var.lVal >= ARRAYSIZE(g_groupname_map))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else if (var.lVal != _iPrivilegeLevel)
|
|
{
|
|
NET_API_STATUS nasRet;
|
|
TCHAR szDomainAndName[256];
|
|
LOCALGROUP_MEMBERS_INFO_3 lgrmi3;
|
|
|
|
// First add the user to their new group
|
|
|
|
wnsprintf(szDomainAndName,
|
|
ARRAYSIZE(szDomainAndName),
|
|
TEXT("%s\\%s"),
|
|
_szDomain,
|
|
_szLoginName);
|
|
|
|
lgrmi3.lgrmi3_domainandname = szDomainAndName;
|
|
|
|
nasRet = NetLocalGroupAddMembers(
|
|
NULL,
|
|
g_groupname_map[var.lVal].szActualGroupName,
|
|
3,
|
|
(LPBYTE)&lgrmi3,
|
|
1);
|
|
|
|
// If we were successful in adding to the group or
|
|
// they were already in the group ...
|
|
if ( nasRet == NERR_Success || nasRet == ERROR_MEMBER_IN_ALIAS )
|
|
{
|
|
// remember the new privilege level
|
|
_iPrivilegeLevel = var.lVal;
|
|
|
|
// remove them from all more-privileged groups
|
|
|
|
for (int i = var.lVal+1; i < ARRAYSIZE(g_groupname_map); i++)
|
|
{
|
|
// "Power Users" doesn't exist on Personal, so this will
|
|
// fail sometimes.
|
|
|
|
NetLocalGroupDelMembers(
|
|
NULL,
|
|
g_groupname_map[i].szActualGroupName,
|
|
3,
|
|
(LPBYTE)&lgrmi3,
|
|
1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(nasRet);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CLogonUser::_LookupUserSid()
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (NULL == _pszSID)
|
|
{
|
|
BYTE rgSidBuffer[sizeof(SID) + (SID_MAX_SUB_AUTHORITIES-1)*sizeof(ULONG)];
|
|
PSID pSid = (PSID)rgSidBuffer;
|
|
DWORD cbSid = sizeof(rgSidBuffer);
|
|
TCHAR szDomainName[MAX_PATH];
|
|
DWORD cbDomainName = ARRAYSIZE(szDomainName);
|
|
SID_NAME_USE snu;
|
|
|
|
if (LookupAccountName(
|
|
(TEXT('\0') != _szDomain[0]) ? _szDomain : NULL,
|
|
_szLoginName,
|
|
pSid,
|
|
&cbSid,
|
|
szDomainName,
|
|
&cbDomainName,
|
|
&snu))
|
|
{
|
|
ConvertSidToStringSid(pSid, &_pszSID);
|
|
}
|
|
}
|
|
|
|
if (NULL == _pszSID)
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CLogonUser::_GetSID(VARIANT* pvar)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (pvar)
|
|
{
|
|
hr = _LookupUserSid();
|
|
|
|
if (NULL != _pszSID)
|
|
{
|
|
pvar->vt = VT_BSTR;
|
|
pvar->bstrVal = SysAllocString(_pszSID);
|
|
hr = pvar->bstrVal ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
DWORD CLogonUser::_GetExpiryDays (HKEY hKeyCurrentUser)
|
|
|
|
{
|
|
DWORD dwDays;
|
|
DWORD dwDataType;
|
|
DWORD dwData;
|
|
DWORD dwDataSize;
|
|
HKEY hKey;
|
|
|
|
static const TCHAR s_szBaseKeyName[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail");
|
|
static const TCHAR s_szMessageExpiryValueName[] = TEXT("MessageExpiryDays");
|
|
|
|
dwDays = 3;
|
|
if (RegOpenKeyEx(hKeyCurrentUser,
|
|
s_szBaseKeyName,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey) == ERROR_SUCCESS)
|
|
{
|
|
dwDataSize = sizeof(dwData);
|
|
if ((RegQueryValueEx(hKey,
|
|
s_szMessageExpiryValueName,
|
|
NULL,
|
|
&dwDataType,
|
|
reinterpret_cast<LPBYTE>(&dwData),
|
|
&dwDataSize) == ERROR_SUCCESS) &&
|
|
(dwDataType == REG_DWORD) &&
|
|
(dwData <= 30))
|
|
{
|
|
dwDays = dwData;
|
|
}
|
|
TBOOL(RegCloseKey(hKey));
|
|
}
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
s_szBaseKeyName,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey))
|
|
{
|
|
dwDataSize = sizeof(dwData);
|
|
if ((RegQueryValueEx(hKey,
|
|
s_szMessageExpiryValueName,
|
|
NULL,
|
|
&dwDataType,
|
|
reinterpret_cast<LPBYTE>(&dwData),
|
|
&dwDataSize) == ERROR_SUCCESS) &&
|
|
(dwDataType == REG_DWORD) &&
|
|
(dwData <= 30))
|
|
{
|
|
dwDays = dwData;
|
|
}
|
|
TBOOL(RegCloseKey(hKey));
|
|
}
|
|
return(dwDays);
|
|
}
|
|
|
|
STDMETHODIMP CLogonUser::getMailAccountInfo(UINT uiAccountIndex, VARIANT *pvarAccountName, UINT *pcUnreadMessages)
|
|
{
|
|
HRESULT hr;
|
|
|
|
DWORD dwComputerNameSize;
|
|
TCHAR szComputerName[CNLEN + sizeof('\0')];
|
|
|
|
hr = E_FAIL;
|
|
|
|
// Only do this for local computer accounts.
|
|
|
|
dwComputerNameSize = ARRAYSIZE(szComputerName);
|
|
if ((GetComputerName(szComputerName, &dwComputerNameSize) != FALSE) &&
|
|
(lstrcmpi(szComputerName, _szDomain) == 0))
|
|
{
|
|
CUserProfile profile(_szLoginName, _szDomain);
|
|
|
|
if (static_cast<HKEY>(profile) != NULL)
|
|
{
|
|
DWORD dwCount;
|
|
TCHAR szMailAccountName[100];
|
|
|
|
hr = SHEnumerateUnreadMailAccounts(profile, uiAccountIndex, szMailAccountName, ARRAYSIZE(szMailAccountName));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (pvarAccountName)
|
|
{
|
|
pvarAccountName->vt = VT_BSTR;
|
|
pvarAccountName->bstrVal = SysAllocString(szMailAccountName);
|
|
hr = pvarAccountName->bstrVal ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && pcUnreadMessages)
|
|
{
|
|
FILETIME ft, ftCurrent;
|
|
SYSTEMTIME st;
|
|
|
|
BOOL ftExpired = false;
|
|
DWORD dwExpiryDays = _GetExpiryDays(profile);
|
|
|
|
hr = SHGetUnreadMailCount(profile, szMailAccountName, &dwCount, &ft, NULL, 0);
|
|
IncrementFILETIME(&ft, FT_ONEDAY * dwExpiryDays);
|
|
GetLocalTime(&st);
|
|
SystemTimeToFileTime(&st, &ftCurrent);
|
|
|
|
ftExpired = ((CompareFileTime(&ft, &ftCurrent) < 0) || (dwExpiryDays == 0));
|
|
|
|
if (SUCCEEDED(hr) && !ftExpired)
|
|
{
|
|
*pcUnreadMessages = dwCount;
|
|
}
|
|
else
|
|
{
|
|
*pcUnreadMessages = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_GetUnreadMail(VARIANT* pvar)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (pvar)
|
|
{
|
|
DWORD dwComputerNameSize;
|
|
TCHAR szComputerName[CNLEN + sizeof('\0')];
|
|
|
|
hr = E_FAIL;
|
|
|
|
// Only do this for local computer accounts.
|
|
|
|
dwComputerNameSize = ARRAYSIZE(szComputerName);
|
|
if ((GetComputerName(szComputerName, &dwComputerNameSize) != FALSE) &&
|
|
(lstrcmpi(szComputerName, _szDomain) == 0))
|
|
{
|
|
CUserProfile profile(_szLoginName, _szDomain);
|
|
|
|
if (static_cast<HKEY>(profile) != NULL)
|
|
{
|
|
DWORD dwCount;
|
|
FILETIME ftFilter;
|
|
SYSTEMTIME st;
|
|
DWORD dwExpiryDays = _GetExpiryDays(profile);
|
|
|
|
GetLocalTime(&st);
|
|
SystemTimeToFileTime(&st, &ftFilter);
|
|
DecrementFILETIME(&ftFilter, FT_ONEDAY * dwExpiryDays);
|
|
|
|
hr = SHGetUnreadMailCount(profile, NULL, &dwCount, &ftFilter, NULL, 0);
|
|
if (SUCCEEDED(hr) && (dwExpiryDays != 0))
|
|
{
|
|
pvar->vt = VT_UI4;
|
|
pvar->uintVal = dwCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_InitPicture()
|
|
{
|
|
HRESULT hr;
|
|
|
|
lstrcpyn(_szPicture, TEXT("file://"), ARRAYSIZE(_szPicture));
|
|
hr = SHGetUserPicturePath(_szLoginName, SHGUPP_FLAG_CREATE, &_szPicture[7]);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
_szPicture[0] = TEXT('\0');
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CLogonUser::_SetPicture(LPCTSTR pszNewPicturePath)
|
|
{
|
|
// use shell32!SHSetUserPicturePath to set the user's
|
|
// picture path. If this is successful then update the
|
|
// _szPicture member variable.
|
|
|
|
HRESULT hr = SHSetUserPicturePath(_szLoginName, 0, pszNewPicturePath);
|
|
if ( S_OK == hr )
|
|
{
|
|
DWORD dwErr;
|
|
HKEY hkUserHint;
|
|
|
|
SysFreeString(_strPictureSource);
|
|
_strPictureSource = SysAllocString(pszNewPicturePath);
|
|
|
|
dwErr = _OpenUserHintKey(KEY_SET_VALUE, &hkUserHint);
|
|
|
|
if ( dwErr == ERROR_SUCCESS )
|
|
{
|
|
if (pszNewPicturePath)
|
|
{
|
|
DWORD cbData = lstrlen(pszNewPicturePath) * sizeof(TCHAR) + sizeof(TEXT('\0'));
|
|
dwErr = RegSetValueEx(hkUserHint,
|
|
c_szPictureSrcVal,
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE)pszNewPicturePath,
|
|
cbData);
|
|
}
|
|
else
|
|
{
|
|
dwErr = RegDeleteValue(hkUserHint, c_szPictureSrcVal);
|
|
}
|
|
|
|
RegCloseKey(hkUserHint);
|
|
}
|
|
|
|
lstrcpyn(_szPicture, TEXT("file://"), ARRAYSIZE(_szPicture));
|
|
hr = SHGetUserPicturePath(_szLoginName, 0, &_szPicture[7]);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
lstrcatn(_szPicture, pszNewPicturePath, ARRAYSIZE(_szPicture));
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
DWORD CLogonUser::_OpenUserHintKey(REGSAM sam, HKEY *phkey)
|
|
{
|
|
DWORD dwErr;
|
|
TCHAR szHintKey[MAX_PATH];
|
|
|
|
// We have to store hint information under HKLM so the logon page can
|
|
// access it, Also, we want to allow non-admins to change their own
|
|
// hints, but non-admins can't write values under HKLM by default.
|
|
//
|
|
// The solution is to use subkeys rather than named values so we can
|
|
// tweak the ACLs on a per-user basis.
|
|
//
|
|
// A non-admin user needs the ability to do 2 things:
|
|
// 1. Create a hint subkey for themselves if one does not exist.
|
|
// 2. Modify the hint contained in their subkey if one already exists.
|
|
//
|
|
// At install time, we set the ACL on the Hints key to allow
|
|
// Authenticated Users KEY_CREATE_SUB_KEY access. Thus, a user is
|
|
// able to create a hint for themselves if one doesn't exist.
|
|
//
|
|
// Immediately after creating a hint subkey, whether it was created
|
|
// by the target user or an admin, we grant the target user
|
|
// KEY_SET_VALUE access to the subkey. This ensures that a user can
|
|
// modify their own hint no matter who created it for them.
|
|
//
|
|
// Note that we don't call RegCreateKeyEx or SHSetValue since we
|
|
// don't want the key to be automatically created here.
|
|
//
|
|
// Note that admins are able to create and modify hints for any user,
|
|
// but a non-admin is only able to create or modify their own hint.
|
|
|
|
// First assume the hint already exists and just try to open it.
|
|
PathCombine(szHintKey, c_szRegRoot, _szLoginName);
|
|
dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
szHintKey,
|
|
0,
|
|
sam,
|
|
phkey);
|
|
if ( dwErr == ERROR_FILE_NOT_FOUND )
|
|
{
|
|
HKEY hkHints;
|
|
|
|
// The hint subkey doesn't exist yet for this user.
|
|
// Try to create one.
|
|
|
|
// Open the Hints key for KEY_CREATE_SUB_KEY
|
|
dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
c_szRegRoot,
|
|
0,
|
|
KEY_CREATE_SUB_KEY,
|
|
&hkHints);
|
|
if ( dwErr == ERROR_SUCCESS )
|
|
{
|
|
// Create a subkey for this user
|
|
dwErr = RegCreateKeyEx(hkHints,
|
|
_szLoginName,
|
|
0,
|
|
NULL,
|
|
0,
|
|
sam,
|
|
NULL,
|
|
phkey,
|
|
NULL);
|
|
if ( dwErr == ERROR_SUCCESS )
|
|
{
|
|
// Grant KEY_SET_VALUE access to the user so they can
|
|
// change their own hint.
|
|
_LookupUserSid();
|
|
if (NULL != _pszSID)
|
|
{
|
|
TCHAR szKey[MAX_PATH];
|
|
TCHAR szSD[MAX_PATH];
|
|
|
|
PathCombine(szKey, TEXT("MACHINE"), szHintKey);
|
|
wnsprintf(szSD,
|
|
ARRAYSIZE(szSD),
|
|
TEXT("D:(A;;0x2;;;%s)"), // 0x2 = KEY_SET_VALUE
|
|
_pszSID);
|
|
SetDacl(szKey, SE_REGISTRY_KEY, szSD);
|
|
}
|
|
}
|
|
RegCloseKey(hkHints);
|
|
}
|
|
}
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CLogonUser::get_isPasswordResetAvailable(VARIANT_BOOL* pbResetAvailable)
|
|
{
|
|
DWORD dwResult;
|
|
|
|
if (!pbResetAvailable)
|
|
return E_POINTER;
|
|
|
|
*pbResetAvailable = VARIANT_FALSE;
|
|
|
|
if (0 == PRQueryStatus(NULL, _szLoginName, &dwResult))
|
|
{
|
|
if (0 == dwResult)
|
|
{
|
|
*pbResetAvailable = VARIANT_TRUE;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|