Skip to content

Commit 1499e7e

Browse files
committed
[MSGINA] Improve Security and Shutdown dialog features (reactos#8372)
- Respect system policies for showing or hiding: * Security dialog "Lock Workstation", "Log Off", "Shut Down", "Change Password", "Task Manager" buttons; * "Log Off" entry in the Shutdown dialog; - Disable the "Shut Down" Security dialog button, and the "Stand by", "Shut down" entries in the Shutdown dialog, if the logged user doesn't have the SeShutdownPrivilege. - Add other missing `WLX_SHUTDOWN_STATE_*` shutdown flags that are supported by Windows. - Improve the retrieval of shutdown options to be displayed in the "Shut Down" dialog. In particular, don't hardcode any sort of defaults, but let them come from what the user can do (Is logoff allowed? Does (s)he have the rights to shutdown/reboot? etc.). If no shutdown options are available, then simply don't display the dialog! - Don't hardcode the default selected shutdown option. Either it comes from the user's registry and is valid (i.e. corresponds to an existing shutdown option in the dialog), otherwise, fall back to the first option in the dialog. In particular this means: * when opening the "Shut Down" dialog from the C-A-D Security dialog, by default we will select the "Log Off" option, or whatever the user last selected; * when opening the dialog from the "Turn Off" Start Menu item, default to the "Shut down" option, or whatever the user last selected. * when opening the dialog from the C-A-D "Log On" dialog (no user is logged in), the "Shut down" option will also be selected, or whatever the system operator last selected. - For the shell-invokable `ShellShutdownDialog()` function, implement support for using a custom display user name, or the one in the `"Logon User Name"` registry value saved by `WlxActivateUserShell()`. Plus, the 3rd parameter specifies the list of shutdown options to *exclude* from the options list.
1 parent 87df3ea commit 1499e7e

File tree

5 files changed

+456
-233
lines changed

5 files changed

+456
-233
lines changed

dll/win32/msgina/gui.c

Lines changed: 102 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010

1111
#include <wingdi.h>
1212
#include <winnls.h>
13-
#include <winreg.h>
1413
#include <ndk/exfuncs.h>
15-
#include <ndk/setypes.h>
1614

1715
typedef struct _DISPLAYSTATUSMSG
1816
{
@@ -133,8 +131,6 @@ SetWelcomeText(HWND hWnd)
133131
DWORD BufSize, dwType, dwWelcomeSize, dwTitleLength;
134132
LONG rc;
135133

136-
TRACE("SetWelcomeText(%p)\n", hWnd);
137-
138134
/* Open the Winlogon key */
139135
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
140136
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
@@ -529,8 +525,6 @@ static VOID
529525
GUIDisplaySASNotice(
530526
IN OUT PGINA_CONTEXT pgContext)
531527
{
532-
TRACE("GUIDisplaySASNotice()\n");
533-
534528
/* Display the notice window */
535529
pgContext->pWlxFuncs->WlxDialogBoxParam(pgContext->hWlx,
536530
pgContext->hDllInstance,
@@ -816,9 +810,14 @@ ChangePasswordDialogProc(
816810

817811

818812
static VOID
819-
OnInitSecurityDlg(HWND hwnd,
820-
PGINA_CONTEXT pgContext)
813+
OnInitSecurityDlg(
814+
_In_ HWND hwnd,
815+
_In_ PGINA_CONTEXT pgContext)
821816
{
817+
HKEY hKeyCurrentUser, hKey;
818+
DWORD dwValue;
819+
LONG lRet;
820+
822821
WCHAR Buffer1[256];
823822
WCHAR Buffer2[256];
824823
WCHAR Buffer3[256];
@@ -842,6 +841,74 @@ OnInitSecurityDlg(HWND hwnd,
842841
wsprintfW(Buffer4, Buffer1, Buffer2, Buffer3);
843842

844843
SetDlgItemTextW(hwnd, IDC_SECURITY_LOGONDATE, Buffer4);
844+
845+
lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
846+
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
847+
0,
848+
KEY_QUERY_VALUE, &hKey);
849+
if (lRet == ERROR_SUCCESS)
850+
{
851+
lRet = ReadRegDwordValue(hKey, L"DisableLockWorkstation", &dwValue);
852+
if ((lRet == ERROR_SUCCESS) && !!dwValue)
853+
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_LOCK), FALSE);
854+
855+
RegCloseKey(hKey);
856+
}
857+
858+
/* Open the per-user registry key */
859+
lRet = RegOpenLoggedOnHKCU(pgContext->UserToken,
860+
KEY_QUERY_VALUE,
861+
&hKeyCurrentUser);
862+
if (lRet != ERROR_SUCCESS)
863+
{
864+
/* We couldn't, bail out */
865+
return;
866+
}
867+
868+
lRet = RegOpenKeyExW(hKeyCurrentUser,
869+
L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
870+
0,
871+
KEY_QUERY_VALUE, &hKey);
872+
if (lRet == ERROR_SUCCESS)
873+
{
874+
lRet = ReadRegDwordValue(hKey, L"DisableLockWorkstation", &dwValue);
875+
if ((lRet == ERROR_SUCCESS) && !!dwValue)
876+
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_LOCK), FALSE);
877+
878+
lRet = ReadRegDwordValue(hKey, L"DisableChangePassword", &dwValue);
879+
if ((lRet == ERROR_SUCCESS) && !!dwValue)
880+
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_CHANGEPWD), FALSE);
881+
882+
lRet = ReadRegDwordValue(hKey, L"DisableTaskMgr", &dwValue);
883+
if ((lRet == ERROR_SUCCESS) && !!dwValue)
884+
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_TASKMGR), FALSE);
885+
886+
RegCloseKey(hKey);
887+
}
888+
889+
lRet = RegOpenKeyExW(hKeyCurrentUser,
890+
L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer",
891+
0,
892+
KEY_QUERY_VALUE, &hKey);
893+
if (lRet == ERROR_SUCCESS)
894+
{
895+
lRet = ReadRegDwordValue(hKey, L"NoLogoff", &dwValue);
896+
if ((lRet == ERROR_SUCCESS) && !!dwValue)
897+
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_LOGOFF), FALSE);
898+
899+
// TODO: Disable also if "NoDisconnect" on Terminal Services
900+
/* Disable the "Shutdown" button if the user doesn't have shutdown privilege */
901+
lRet = ReadRegDwordValue(hKey, L"NoClose", &dwValue);
902+
if (((lRet == ERROR_SUCCESS) && !!dwValue) ||
903+
!TestTokenPrivilege(pgContext->UserToken, SE_SHUTDOWN_PRIVILEGE))
904+
{
905+
EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_SHUTDOWN), FALSE);
906+
}
907+
908+
RegCloseKey(hKey);
909+
}
910+
911+
RegCloseKey(hKeyCurrentUser);
845912
}
846913

847914

@@ -918,48 +985,36 @@ OnLogOff(
918985
static
919986
INT
920987
OnShutDown(
921-
IN HWND hwndDlg,
922-
IN PGINA_CONTEXT pgContext)
988+
_In_ HWND hwndDlg,
989+
_In_ PGINA_CONTEXT pgContext,
990+
_In_ DWORD dwExcludeOptions)
923991
{
992+
HKEY hKeyCurrentUser = NULL;
993+
DWORD ShutdownOptions = 0;
924994
INT ret;
925-
DWORD ShutdownOptions;
926-
927-
TRACE("OnShutDown(%p %p)\n", hwndDlg, pgContext);
928995

929-
pgContext->nShutdownAction = GetDefaultShutdownSelState();
930-
ShutdownOptions = GetDefaultShutdownOptions();
931-
932-
if (pgContext->UserToken != NULL)
996+
/* Open the per-user registry key */
997+
if (RegOpenLoggedOnHKCU(pgContext->UserToken,
998+
KEY_QUERY_VALUE | KEY_CREATE_SUB_KEY | KEY_SET_VALUE,
999+
&hKeyCurrentUser) != ERROR_SUCCESS)
9331000
{
934-
if (ImpersonateLoggedOnUser(pgContext->UserToken))
935-
{
936-
pgContext->nShutdownAction = LoadShutdownSelState();
937-
ShutdownOptions = GetAllowedShutdownOptions();
938-
RevertToSelf();
939-
}
940-
else
941-
{
942-
ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
943-
}
1001+
ERR("RegOpenLoggedOnHKCU() failed with error %ld\n", GetLastError());
9441002
}
1003+
pgContext->nShutdownAction = 0;
1004+
if (hKeyCurrentUser)
1005+
{
1006+
ShutdownOptions = GetAllowedShutdownOptions(hKeyCurrentUser, pgContext->UserToken);
1007+
pgContext->nShutdownAction = LoadShutdownSelState(hKeyCurrentUser);
1008+
}
1009+
ShutdownOptions &= ~dwExcludeOptions;
9451010

9461011
ret = ShutdownDialog(hwndDlg, ShutdownOptions, pgContext);
9471012

948-
if (ret == IDOK)
949-
{
950-
if (pgContext->UserToken != NULL)
951-
{
952-
if (ImpersonateLoggedOnUser(pgContext->UserToken))
953-
{
954-
SaveShutdownSelState(pgContext->nShutdownAction);
955-
RevertToSelf();
956-
}
957-
else
958-
{
959-
ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
960-
}
961-
}
962-
}
1013+
if ((ret == IDOK) && hKeyCurrentUser)
1014+
SaveShutdownSelState(hKeyCurrentUser, pgContext->nShutdownAction);
1015+
1016+
if (hKeyCurrentUser)
1017+
RegCloseKey(hKeyCurrentUser);
9631018

9641019
return ret;
9651020
}
@@ -1031,7 +1086,7 @@ SecurityDialogProc(
10311086
RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, Old, FALSE, &Old);
10321087
}
10331088
}
1034-
else if (OnShutDown(hwndDlg, pgContext) == IDOK)
1089+
else if (OnShutDown(hwndDlg, pgContext, 0) == IDOK)
10351090
{
10361091
EndDialog(hwndDlg, pgContext->nShutdownAction);
10371092
}
@@ -1066,8 +1121,6 @@ GUILoggedOnSAS(
10661121
{
10671122
INT result;
10681123

1069-
TRACE("GUILoggedOnSAS()\n");
1070-
10711124
if (dwSasType != WLX_SAS_TYPE_CTRL_ALT_DEL)
10721125
{
10731126
/* Nothing to do for WLX_SAS_TYPE_TIMEOUT; the dialog will
@@ -1377,8 +1430,11 @@ LogonDialogProc(
13771430
return TRUE;
13781431

13791432
case IDC_LOGON_SHUTDOWN:
1380-
if (OnShutDown(hwndDlg, pDlgData->pgContext) == IDOK)
1433+
if (OnShutDown(hwndDlg, pDlgData->pgContext,
1434+
WLX_SHUTDOWN_STATE_DISCONNECT | WLX_SHUTDOWN_STATE_LOGOFF) == IDOK)
1435+
{
13811436
EndDialog(hwndDlg, pDlgData->pgContext->nShutdownAction);
1437+
}
13821438
return TRUE;
13831439
}
13841440
break;
@@ -1730,8 +1786,6 @@ static VOID
17301786
GUIDisplayLockedNotice(
17311787
IN OUT PGINA_CONTEXT pgContext)
17321788
{
1733-
TRACE("GUIdisplayLockedNotice()\n");
1734-
17351789
pgContext->pWlxFuncs->WlxDialogBoxParam(
17361790
pgContext->hWlx,
17371791
pgContext->hDllInstance,

dll/win32/msgina/msgina.h

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ extern "C" {
1919
#include <winuser.h>
2020
#include <winwlx.h>
2121
#include <ndk/rtlfuncs.h>
22+
#include <ndk/setypes.h> // For SE_*_PRIVILEGE
2223
#include <ntsecapi.h>
2324

2425
#include <strsafe.h>
@@ -123,20 +124,35 @@ CreateProfile(
123124

124125
/* shutdown.c */
125126

126-
DWORD
127-
GetDefaultShutdownSelState(VOID);
127+
/**
128+
* @brief Shutdown state flags
129+
* @see
130+
* https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc962586(v=technet.10)
131+
* https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc783367(v=ws.10)
132+
**/
133+
#define WLX_SHUTDOWN_STATE_LOGOFF 0x01 ///< "Log off <username>"
134+
#define WLX_SHUTDOWN_STATE_POWER_OFF 0x02 ///< "Shut down"
135+
#define WLX_SHUTDOWN_STATE_REBOOT 0x04 ///< "Restart"
136+
// 0x08 ///< "Restart in MS-DOS mode" - Yes, WinNT/2k/XP/2k3 msgina.dll/shell32.dll has it!
137+
#define WLX_SHUTDOWN_STATE_SLEEP 0x10 ///< "Stand by"
138+
#define WLX_SHUTDOWN_STATE_SLEEP2 0x20 ///< "Stand by (with wakeup events disabled)"
139+
#define WLX_SHUTDOWN_STATE_HIBERNATE 0x40 ///< "Hibernate"
140+
#define WLX_SHUTDOWN_STATE_DISCONNECT 0x80 ///< "Disconnect" (only available in Terminal Services sessions)
141+
#define WLX_SHUTDOWN_AUTOUPDATE 0x100 ///< Set when updates are queued
128142

129143
DWORD
130-
LoadShutdownSelState(VOID);
144+
LoadShutdownSelState(
145+
_In_ HKEY hKeyCurrentUser);
131146

132147
VOID
133-
SaveShutdownSelState(DWORD ShutdownCode);
134-
135-
DWORD
136-
GetDefaultShutdownOptions(VOID);
148+
SaveShutdownSelState(
149+
_In_ HKEY hKeyCurrentUser,
150+
_In_ DWORD ShutdownCode);
137151

138152
DWORD
139-
GetAllowedShutdownOptions(VOID);
153+
GetAllowedShutdownOptions(
154+
_In_opt_ HKEY hKeyCurrentUser,
155+
_In_opt_ HANDLE hUserToken);
140156

141157
INT_PTR
142158
ShutdownDialog(
@@ -164,6 +180,11 @@ ReadRegDwordValue(
164180
_In_ PCWSTR pszValue,
165181
_Out_ PDWORD pValue);
166182

183+
BOOL
184+
TestTokenPrivilege(
185+
_In_opt_ HANDLE hToken,
186+
_In_ ULONG Privilege);
187+
167188
PWSTR
168189
DuplicateString(
169190
_In_opt_ PCWSTR Str);

0 commit comments

Comments
 (0)