windows-nt/Source/XPSP1/NT/shell/ext/shgina/cuser.cpp

1883 lines
54 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// 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;
}