diff --git a/dll/win32/msgina/gui.c b/dll/win32/msgina/gui.c index 85e3a7fd1b1..7ecaef16f29 100644 --- a/dll/win32/msgina/gui.c +++ b/dll/win32/msgina/gui.c @@ -10,9 +10,7 @@ #include #include -#include #include -#include typedef struct _DISPLAYSTATUSMSG { @@ -133,8 +131,6 @@ SetWelcomeText(HWND hWnd) DWORD BufSize, dwType, dwWelcomeSize, dwTitleLength; LONG rc; - TRACE("SetWelcomeText(%p)\n", hWnd); - /* Open the Winlogon key */ rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", @@ -529,8 +525,6 @@ static VOID GUIDisplaySASNotice( IN OUT PGINA_CONTEXT pgContext) { - TRACE("GUIDisplaySASNotice()\n"); - /* Display the notice window */ pgContext->pWlxFuncs->WlxDialogBoxParam(pgContext->hWlx, pgContext->hDllInstance, @@ -816,9 +810,14 @@ ChangePasswordDialogProc( static VOID -OnInitSecurityDlg(HWND hwnd, - PGINA_CONTEXT pgContext) +OnInitSecurityDlg( + _In_ HWND hwnd, + _In_ PGINA_CONTEXT pgContext) { + HKEY hKeyCurrentUser, hKey; + DWORD dwValue; + LONG lRet; + WCHAR Buffer1[256]; WCHAR Buffer2[256]; WCHAR Buffer3[256]; @@ -842,6 +841,74 @@ OnInitSecurityDlg(HWND hwnd, wsprintfW(Buffer4, Buffer1, Buffer2, Buffer3); SetDlgItemTextW(hwnd, IDC_SECURITY_LOGONDATE, Buffer4); + + lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", + 0, + KEY_QUERY_VALUE, &hKey); + if (lRet == ERROR_SUCCESS) + { + lRet = ReadRegDwordValue(hKey, L"DisableLockWorkstation", &dwValue); + if ((lRet == ERROR_SUCCESS) && !!dwValue) + EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_LOCK), FALSE); + + RegCloseKey(hKey); + } + + /* Open the per-user registry key */ + lRet = RegOpenLoggedOnHKCU(pgContext->UserToken, + KEY_QUERY_VALUE, + &hKeyCurrentUser); + if (lRet != ERROR_SUCCESS) + { + /* We couldn't, bail out */ + return; + } + + lRet = RegOpenKeyExW(hKeyCurrentUser, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", + 0, + KEY_QUERY_VALUE, &hKey); + if (lRet == ERROR_SUCCESS) + { + lRet = ReadRegDwordValue(hKey, L"DisableLockWorkstation", &dwValue); + if ((lRet == ERROR_SUCCESS) && !!dwValue) + EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_LOCK), FALSE); + + lRet = ReadRegDwordValue(hKey, L"DisableChangePassword", &dwValue); + if ((lRet == ERROR_SUCCESS) && !!dwValue) + EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_CHANGEPWD), FALSE); + + lRet = ReadRegDwordValue(hKey, L"DisableTaskMgr", &dwValue); + if ((lRet == ERROR_SUCCESS) && !!dwValue) + EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_TASKMGR), FALSE); + + RegCloseKey(hKey); + } + + lRet = RegOpenKeyExW(hKeyCurrentUser, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", + 0, + KEY_QUERY_VALUE, &hKey); + if (lRet == ERROR_SUCCESS) + { + lRet = ReadRegDwordValue(hKey, L"NoLogoff", &dwValue); + if ((lRet == ERROR_SUCCESS) && !!dwValue) + EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_LOGOFF), FALSE); + + // TODO: Disable also if "NoDisconnect" on Terminal Services + /* Disable the "Shutdown" button if the user doesn't have shutdown privilege */ + lRet = ReadRegDwordValue(hKey, L"NoClose", &dwValue); + if (((lRet == ERROR_SUCCESS) && !!dwValue) || + !TestTokenPrivilege(pgContext->UserToken, SE_SHUTDOWN_PRIVILEGE)) + { + EnableWindow(GetDlgItem(hwnd, IDC_SECURITY_SHUTDOWN), FALSE); + } + + RegCloseKey(hKey); + } + + RegCloseKey(hKeyCurrentUser); } @@ -918,48 +985,36 @@ OnLogOff( static INT OnShutDown( - IN HWND hwndDlg, - IN PGINA_CONTEXT pgContext) + _In_ HWND hwndDlg, + _In_ PGINA_CONTEXT pgContext, + _In_ DWORD dwExcludeOptions) { + HKEY hKeyCurrentUser = NULL; + DWORD ShutdownOptions = 0; INT ret; - DWORD ShutdownOptions; - TRACE("OnShutDown(%p %p)\n", hwndDlg, pgContext); - - pgContext->nShutdownAction = GetDefaultShutdownSelState(); - ShutdownOptions = GetDefaultShutdownOptions(); - - if (pgContext->UserToken != NULL) + /* Open the per-user registry key */ + if (RegOpenLoggedOnHKCU(pgContext->UserToken, + KEY_QUERY_VALUE | KEY_CREATE_SUB_KEY | KEY_SET_VALUE, + &hKeyCurrentUser) != ERROR_SUCCESS) { - if (ImpersonateLoggedOnUser(pgContext->UserToken)) - { - pgContext->nShutdownAction = LoadShutdownSelState(); - ShutdownOptions = GetAllowedShutdownOptions(); - RevertToSelf(); - } - else - { - ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError()); - } + ERR("RegOpenLoggedOnHKCU() failed with error %ld\n", GetLastError()); } + pgContext->nShutdownAction = 0; + if (hKeyCurrentUser) + { + ShutdownOptions = GetAllowedShutdownOptions(hKeyCurrentUser, pgContext->UserToken); + pgContext->nShutdownAction = LoadShutdownSelState(hKeyCurrentUser); + } + ShutdownOptions &= ~dwExcludeOptions; ret = ShutdownDialog(hwndDlg, ShutdownOptions, pgContext); - if (ret == IDOK) - { - if (pgContext->UserToken != NULL) - { - if (ImpersonateLoggedOnUser(pgContext->UserToken)) - { - SaveShutdownSelState(pgContext->nShutdownAction); - RevertToSelf(); - } - else - { - ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError()); - } - } - } + if ((ret == IDOK) && hKeyCurrentUser) + SaveShutdownSelState(hKeyCurrentUser, pgContext->nShutdownAction); + + if (hKeyCurrentUser) + RegCloseKey(hKeyCurrentUser); return ret; } @@ -1031,7 +1086,7 @@ SecurityDialogProc( RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, Old, FALSE, &Old); } } - else if (OnShutDown(hwndDlg, pgContext) == IDOK) + else if (OnShutDown(hwndDlg, pgContext, 0) == IDOK) { EndDialog(hwndDlg, pgContext->nShutdownAction); } @@ -1066,8 +1121,6 @@ GUILoggedOnSAS( { INT result; - TRACE("GUILoggedOnSAS()\n"); - if (dwSasType != WLX_SAS_TYPE_CTRL_ALT_DEL) { /* Nothing to do for WLX_SAS_TYPE_TIMEOUT; the dialog will @@ -1377,8 +1430,11 @@ LogonDialogProc( return TRUE; case IDC_LOGON_SHUTDOWN: - if (OnShutDown(hwndDlg, pDlgData->pgContext) == IDOK) + if (OnShutDown(hwndDlg, pDlgData->pgContext, + WLX_SHUTDOWN_STATE_DISCONNECT | WLX_SHUTDOWN_STATE_LOGOFF) == IDOK) + { EndDialog(hwndDlg, pDlgData->pgContext->nShutdownAction); + } return TRUE; } break; @@ -1730,8 +1786,6 @@ static VOID GUIDisplayLockedNotice( IN OUT PGINA_CONTEXT pgContext) { - TRACE("GUIdisplayLockedNotice()\n"); - pgContext->pWlxFuncs->WlxDialogBoxParam( pgContext->hWlx, pgContext->hDllInstance, diff --git a/dll/win32/msgina/msgina.h b/dll/win32/msgina/msgina.h index fa15132ec72..07bd7d08f14 100644 --- a/dll/win32/msgina/msgina.h +++ b/dll/win32/msgina/msgina.h @@ -19,6 +19,7 @@ extern "C" { #include #include #include +#include // For SE_*_PRIVILEGE #include #include @@ -123,20 +124,35 @@ CreateProfile( /* shutdown.c */ -DWORD -GetDefaultShutdownSelState(VOID); +/** + * @brief Shutdown state flags + * @see + * https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc962586(v=technet.10) + * https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc783367(v=ws.10) + **/ +#define WLX_SHUTDOWN_STATE_LOGOFF 0x01 ///< "Log off " +#define WLX_SHUTDOWN_STATE_POWER_OFF 0x02 ///< "Shut down" +#define WLX_SHUTDOWN_STATE_REBOOT 0x04 ///< "Restart" +// 0x08 ///< "Restart in MS-DOS mode" - Yes, WinNT/2k/XP/2k3 msgina.dll/shell32.dll has it! +#define WLX_SHUTDOWN_STATE_SLEEP 0x10 ///< "Stand by" +#define WLX_SHUTDOWN_STATE_SLEEP2 0x20 ///< "Stand by (with wakeup events disabled)" +#define WLX_SHUTDOWN_STATE_HIBERNATE 0x40 ///< "Hibernate" +#define WLX_SHUTDOWN_STATE_DISCONNECT 0x80 ///< "Disconnect" (only available in Terminal Services sessions) +#define WLX_SHUTDOWN_AUTOUPDATE 0x100 ///< Set when updates are queued DWORD -LoadShutdownSelState(VOID); +LoadShutdownSelState( + _In_ HKEY hKeyCurrentUser); VOID -SaveShutdownSelState(DWORD ShutdownCode); +SaveShutdownSelState( + _In_ HKEY hKeyCurrentUser, + _In_ DWORD ShutdownCode); DWORD -GetDefaultShutdownOptions(VOID); - -DWORD -GetAllowedShutdownOptions(VOID); +GetAllowedShutdownOptions( + _In_opt_ HKEY hKeyCurrentUser, + _In_opt_ HANDLE hUserToken); INT_PTR ShutdownDialog( @@ -164,6 +180,11 @@ ReadRegDwordValue( _In_ PCWSTR pszValue, _Out_ PDWORD pValue); +BOOL +TestTokenPrivilege( + _In_opt_ HANDLE hToken, + _In_ ULONG Privilege); + PWSTR DuplicateString( _In_opt_ PCWSTR Str); diff --git a/dll/win32/msgina/shutdown.c b/dll/win32/msgina/shutdown.c index 44b2aab1719..5a99c9a22ac 100644 --- a/dll/win32/msgina/shutdown.c +++ b/dll/win32/msgina/shutdown.c @@ -14,17 +14,7 @@ #include #include -/* Shutdown state flags */ -#define WLX_SHUTDOWN_STATE_LOGOFF 0x01 -#define WLX_SHUTDOWN_STATE_POWER_OFF 0x02 -#define WLX_SHUTDOWN_STATE_REBOOT 0x04 -// 0x08 -#define WLX_SHUTDOWN_STATE_SLEEP 0x10 -// 0x20 -#define WLX_SHUTDOWN_STATE_HIBERNATE 0x40 -// 0x80 - -/* Macros for fancy shut down dialog */ +/* Macros for fancy shutdown dialog */ #define FONT_POINT_SIZE 13 #define DARK_GREY_COLOR RGB(244, 244, 244) @@ -73,6 +63,61 @@ typedef struct _SHUTDOWN_DLG_CONTEXT WNDPROC OldButtonProc; } SHUTDOWN_DLG_CONTEXT, *PSHUTDOWN_DLG_CONTEXT; + +/** + * @brief + * Looks up a policy value on both HKEY_LOCAL_MACHINE + * and HKEY_CURRENT_USER, under the same policy sub-key. + * + * @note + * HKEY_LOCAL_MACHINE policies trump over HKEY_CURRENT_USER ones. + **/ +LONG +GetPolicyDWORDValue( + _In_opt_ HKEY hKeyCurrentUser, + _In_ PCWSTR pPolicyKey, + _In_ PCWSTR ValueName, + _Out_ PDWORD pValue) +{ + HKEY hKey; + DWORD dwValue; + LONG rc; + + *pValue = 0; + + /* Query the policy value in HKEY_LOCAL_MACHINE */ + rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, pPolicyKey, 0, + KEY_QUERY_VALUE, &hKey); + if (rc == ERROR_SUCCESS) + { + rc = ReadRegDwordValue(hKey, ValueName, &dwValue); + RegCloseKey(hKey); + if (rc == ERROR_SUCCESS) + { + *pValue = dwValue; + return ERROR_SUCCESS; + } + } + + /* If not found, query the policy value in HKEY_CURRENT_USER */ + rc = RegOpenKeyExW(hKeyCurrentUser ? hKeyCurrentUser : HKEY_CURRENT_USER, + pPolicyKey, 0, + KEY_QUERY_VALUE, &hKey); + if (rc == ERROR_SUCCESS) + { + rc = ReadRegDwordValue(hKey, ValueName, &dwValue); + RegCloseKey(hKey); + if (rc == ERROR_SUCCESS) + { + *pValue = dwValue; + return ERROR_SUCCESS; + } + } + + return ERROR_NOT_FOUND; +} + + static BOOL GetShutdownReasonUI(VOID) @@ -431,78 +476,6 @@ ShellIsFriendlyUIActive(VOID) return bActive; } -DWORD -GetDefaultShutdownSelState(VOID) -{ - return WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; -} - -DWORD -LoadShutdownSelState(VOID) -{ - LONG lRet; - HKEY hKeyCurrentUser, hKey; - DWORD dwValue, dwTemp, dwSize; - - /* Default to shutdown */ - dwValue = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; - - /* Open the current user HKCU key */ - lRet = RegOpenCurrentUser(MAXIMUM_ALLOWED, &hKeyCurrentUser); - if (lRet == ERROR_SUCCESS) - { - /* Open the subkey */ - lRet = RegOpenKeyExW(hKeyCurrentUser, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", - 0, KEY_QUERY_VALUE, &hKey); - RegCloseKey(hKeyCurrentUser); - } - if (lRet != ERROR_SUCCESS) - return dwValue; - - /* Read the value */ - dwSize = sizeof(dwTemp); - lRet = RegQueryValueExW(hKey, - L"Shutdown Setting", - NULL, NULL, - (LPBYTE)&dwTemp, &dwSize); - RegCloseKey(hKey); - - if (lRet == ERROR_SUCCESS) - { - switch (dwTemp) - { - case WLX_SHUTDOWN_STATE_LOGOFF: - dwValue = WLX_SAS_ACTION_LOGOFF; - break; - - case WLX_SHUTDOWN_STATE_POWER_OFF: - dwValue = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; - break; - - case WLX_SHUTDOWN_STATE_REBOOT: - dwValue = WLX_SAS_ACTION_SHUTDOWN_REBOOT; - break; - - // 0x08 - - case WLX_SHUTDOWN_STATE_SLEEP: - dwValue = WLX_SAS_ACTION_SHUTDOWN_SLEEP; - break; - - // 0x20 - - case WLX_SHUTDOWN_STATE_HIBERNATE: - dwValue = WLX_SAS_ACTION_SHUTDOWN_HIBERNATE; - break; - - // 0x80 - } - } - - return dwValue; -} - static INT_PTR CALLBACK OwnerDrawButtonSubclass( @@ -719,27 +692,85 @@ VOID OnTimer( } } -VOID -SaveShutdownSelState( - IN DWORD ShutdownCode) +DWORD +LoadShutdownSelState( + _In_ HKEY hKeyCurrentUser) { + HKEY hKey; + DWORD dwValue, dwTemp, dwSize; LONG lRet; - HKEY hKeyCurrentUser, hKey; - DWORD dwValue = 0; - /* Open the current user HKCU key */ - lRet = RegOpenCurrentUser(MAXIMUM_ALLOWED, &hKeyCurrentUser); + /* Default to first item */ + dwValue = 0; + + /* Open the subkey */ + lRet = RegOpenKeyExW(hKeyCurrentUser, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", + 0, KEY_QUERY_VALUE, &hKey); + if (lRet != ERROR_SUCCESS) + return dwValue; + + /* Read the value */ + dwSize = sizeof(dwTemp); + lRet = RegQueryValueExW(hKey, + L"Shutdown Setting", + NULL, NULL, + (LPBYTE)&dwTemp, &dwSize); + RegCloseKey(hKey); + if (lRet == ERROR_SUCCESS) { - /* Create the subkey */ - lRet = RegCreateKeyExW(hKeyCurrentUser, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", - 0, NULL, - REG_OPTION_NON_VOLATILE, - KEY_SET_VALUE, - NULL, &hKey, NULL); - RegCloseKey(hKeyCurrentUser); + switch (dwTemp) + { + case WLX_SHUTDOWN_STATE_LOGOFF: + dwValue = WLX_SAS_ACTION_LOGOFF; + break; + + case WLX_SHUTDOWN_STATE_POWER_OFF: + dwValue = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; + break; + + case WLX_SHUTDOWN_STATE_REBOOT: + dwValue = WLX_SAS_ACTION_SHUTDOWN_REBOOT; + break; + + // 0x08 + + case WLX_SHUTDOWN_STATE_SLEEP: + dwValue = WLX_SAS_ACTION_SHUTDOWN_SLEEP; + break; + + case WLX_SHUTDOWN_STATE_SLEEP2: + dwValue = WLX_SAS_ACTION_SHUTDOWN_SLEEP2; + break; + + case WLX_SHUTDOWN_STATE_HIBERNATE: + dwValue = WLX_SAS_ACTION_SHUTDOWN_HIBERNATE; + break; + + // WLX_SHUTDOWN_STATE_DISCONNECT + } } + + return dwValue; +} + +VOID +SaveShutdownSelState( + _In_ HKEY hKeyCurrentUser, + _In_ DWORD ShutdownCode) +{ + HKEY hKey; + DWORD dwValue = 0; + LONG lRet; + + /* Create the subkey */ + lRet = RegCreateKeyExW(hKeyCurrentUser, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", + 0, NULL, + REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, + NULL, &hKey, NULL); if (lRet != ERROR_SUCCESS) return; @@ -774,18 +805,38 @@ SaveShutdownSelState( } DWORD -GetDefaultShutdownOptions(VOID) -{ - return WLX_SHUTDOWN_STATE_POWER_OFF | WLX_SHUTDOWN_STATE_REBOOT; -} - -DWORD -GetAllowedShutdownOptions(VOID) +GetAllowedShutdownOptions( + _In_opt_ HKEY hKeyCurrentUser, + _In_opt_ HANDLE hUserToken) { DWORD Options = 0; + DWORD dwPolicyValue; - // FIXME: Compute those options accordings to current user's rights! - Options |= WLX_SHUTDOWN_STATE_LOGOFF | WLX_SHUTDOWN_STATE_POWER_OFF | WLX_SHUTDOWN_STATE_REBOOT; + dwPolicyValue = 0; + GetPolicyDWORDValue(hKeyCurrentUser, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", + L"NoLogoff", &dwPolicyValue); + if (dwPolicyValue == 0) + Options |= WLX_SHUTDOWN_STATE_LOGOFF; + +#if 0 // TODO: Enable and lookup on a Terminal Services session + dwPolicyValue = 0; + GetPolicyDWORDValue(hKeyCurrentUser, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", + L"NoDisconnect", &dwPolicyValue); + if (dwPolicyValue == 0) + Options |= WLX_SHUTDOWN_STATE_DISCONNECT; +#endif + + /* Add shutdown options only if the user has shutdown privilege */ + if (!TestTokenPrivilege(hUserToken, SE_SHUTDOWN_PRIVILEGE)) + return Options; // The user doesn't have them, bail out. + + Options |= WLX_SHUTDOWN_STATE_POWER_OFF | WLX_SHUTDOWN_STATE_REBOOT; + + // NOTE: "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Power" value "Shutdown" + // for "advanced" sleep options. See the 3rd parameter of: + // https://learn.microsoft.com/en-us/windows/win32/api/powrprof/nf-powrprof-setsuspendstate if (IsPwrSuspendAllowed()) Options |= WLX_SHUTDOWN_STATE_SLEEP; @@ -793,24 +844,34 @@ GetAllowedShutdownOptions(VOID) if (IsPwrHibernateAllowed()) Options |= WLX_SHUTDOWN_STATE_HIBERNATE; + // TODO: Consider Windows 8+ support for: + // "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Power" value "HiberbootEnabled" + + // TODO: Add WLX_SHUTDOWN_AUTOUPDATE if: + // - Policy "Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU" value "NoAUShutdownOption" + // is absent or zero; + // - SystemBatteryState detects the computer is on external power; + // - wuaueng.dll!ordinal_4 tells there are pending updates. + return Options; } static VOID UpdateShutdownDesc( - IN HWND hDlg, - IN PSHUTDOWN_DLG_CONTEXT pContext) // HINSTANCE hInstance + _In_ HWND hDlg, + _In_ PSHUTDOWN_DLG_CONTEXT pContext) // HINSTANCE hInstance { - UINT DescId = 0; + HWND hwndList = GetDlgItem(hDlg, IDC_SHUTDOWN_ACTION); DWORD ShutdownCode; + UINT DescId = 0; WCHAR szBuffer[256]; - ShutdownCode = SendDlgItemMessageW(hDlg, IDC_SHUTDOWN_ACTION, CB_GETCURSEL, 0, 0); + ShutdownCode = SendMessageW(hwndList, CB_GETCURSEL, 0, 0); + if (ShutdownCode != CB_ERR) + ShutdownCode = SendMessageW(hwndList, CB_GETITEMDATA, ShutdownCode, 0); if (ShutdownCode == CB_ERR) // Invalid selection return; - ShutdownCode = SendDlgItemMessageW(hDlg, IDC_SHUTDOWN_ACTION, CB_GETITEMDATA, ShutdownCode, 0); - switch (ShutdownCode) { case WLX_SAS_ACTION_LOGOFF: @@ -842,9 +903,10 @@ UpdateShutdownDesc( if (pContext->bReasonUI) { - EnableWindow(GetDlgItem(hDlg, IDC_REASON_PLANNED), (ShutdownCode != WLX_SAS_ACTION_LOGOFF)); - EnableWindow(GetDlgItem(hDlg, IDC_REASON_LIST), (ShutdownCode != WLX_SAS_ACTION_LOGOFF)); - EnableWindow(GetDlgItem(hDlg, IDC_REASON_COMMENT), (ShutdownCode != WLX_SAS_ACTION_LOGOFF)); + BOOL bNotLogoff = (ShutdownCode != WLX_SAS_ACTION_LOGOFF); + EnableWindow(GetDlgItem(hDlg, IDC_REASON_PLANNED), bNotLogoff); + EnableWindow(GetDlgItem(hDlg, IDC_REASON_LIST), bNotLogoff); + EnableWindow(GetDlgItem(hDlg, IDC_REASON_COMMENT), bNotLogoff); } } @@ -864,7 +926,7 @@ ShutdownOnFriendlyInit( pContext->hfFont = CreateFontW(lfHeight, 0, 0, 0, FW_MEDIUM, FALSE, 0, 0, 0, 0, 0, 0, 0, L"MS Shell Dlg"); SendDlgItemMessageW(hDlg, IDC_TURN_OFF_STATIC, WM_SETFONT, (WPARAM)pContext->hfFont, TRUE); - /* Create a brush for static controls for fancy shut down dialog */ + /* Create a brush for static controls for fancy shutdown dialog */ pContext->hBrush = CreateSolidBrush(DARK_GREY_COLOR); /* Gather image strip */ @@ -932,7 +994,7 @@ ShutdownOnInit( SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_LOGOFF); } - /* Shut down - DEFAULT */ + /* Shut down */ if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_POWER_OFF) { LoadStringW(pgContext->hDllInstance, IDS_SHUTDOWN_SHUTDOWN, szBuffer, _countof(szBuffer)); @@ -961,7 +1023,7 @@ ShutdownOnInit( SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_SLEEP); } - // if (pContext->ShutdownOptions & 0x20) {} + // if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_SLEEP2) {} /* Hibernate */ if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_HIBERNATE) @@ -972,9 +1034,11 @@ ShutdownOnInit( SendMessageW(hwndList, CB_SETITEMDATA, idx, WLX_SAS_ACTION_SHUTDOWN_HIBERNATE); } - // if (pContext->ShutdownOptions & 0x80) {} + // if (pContext->ShutdownOptions & WLX_SHUTDOWN_STATE_DISCONNECT) {} - /* Set the default shut down selection */ + // if (pContext->ShutdownOptions & WLX_SHUTDOWN_AUTOUPDATE) {} + + /* Set the default selection */ count = SendMessageW(hwndList, CB_GETCOUNT, 0, 0); for (i = 0; i < count; i++) { @@ -984,6 +1048,11 @@ ShutdownOnInit( break; } } + /* If no item was selected (nShutdownAction with value 0 or invalid, + * or an action that is currently unavailable), set the selection to + * the first list item. */ + if (count && (SendMessageW(hwndList, CB_GETCURSEL, 0, 0) == CB_ERR)) + SendMessageW(hwndList, CB_SETCURSEL, 0, 0); /* Update the choice description based on the current selection */ UpdateShutdownDesc(hDlg, pContext); @@ -991,24 +1060,16 @@ ShutdownOnInit( static VOID ShutdownOnOk( - IN HWND hDlg, - IN PGINA_CONTEXT pgContext) + _In_ HWND hDlg, + _In_ PGINA_CONTEXT pgContext) { - INT idx; - - idx = SendDlgItemMessageW(hDlg, - IDC_SHUTDOWN_ACTION, - CB_GETCURSEL, - 0, - 0); - if (idx != CB_ERR) + HWND hwndList = GetDlgItem(hDlg, IDC_SHUTDOWN_ACTION); + DWORD ShutdownCode = SendMessageW(hwndList, CB_GETCURSEL, 0, 0); + if (ShutdownCode != CB_ERR) { - pgContext->nShutdownAction = - SendDlgItemMessageW(hDlg, - IDC_SHUTDOWN_ACTION, - CB_GETITEMDATA, - idx, - 0); + ShutdownCode = SendMessageW(hwndList, CB_GETITEMDATA, ShutdownCode, 0); + if (ShutdownCode != CB_ERR) + pgContext->nShutdownAction = ShutdownCode; } } @@ -1030,7 +1091,6 @@ ShutdownDialogProc( { pContext = (PSHUTDOWN_DLG_CONTEXT)lParam; SetWindowLongPtrW(hDlg, GWLP_USERDATA, (LONG_PTR)pContext); - ShutdownOnInit(hDlg, pContext); return TRUE; } @@ -1153,13 +1213,15 @@ ShutdownDialog( SHUTDOWN_DLG_CONTEXT Context = {0}; #if 0 - DWORD ShutdownOptions; - - // FIXME: User impersonation!! - pgContext->nShutdownAction = LoadShutdownSelState(); - ShutdownOptions = GetAllowedShutdownOptions(); + ShutdownOptions = GetAllowedShutdownOptions(???, pgContext->UserToken); + pgContext->nShutdownAction = LoadShutdownSelState(???); + ShutdownOptions &= ~dwExcludeOptions; #endif + /* Bail out early if no shutdown options are available */ + if (ShutdownOptions == 0) + return IDCANCEL; + Context.pgContext = pgContext; Context.ShutdownOptions = ShutdownOptions; Context.ShutdownDialogId = IDD_SHUTDOWN; @@ -1181,13 +1243,9 @@ ShutdownDialog( if (Context.bFriendlyUI) { if (IsShowHibernateButtonActive()) - { Context.ShutdownDialogId = IDD_SHUTDOWN_FANCY_LONG; - } else - { Context.ShutdownDialogId = IDD_SHUTDOWN_FANCY; - } } ret = DialogBoxParamW(pgContext->hDllInstance, @@ -1197,10 +1255,11 @@ ShutdownDialog( (LPARAM)&Context); } + // TODO: Handle shutdown reasons. + #if 0 - // FIXME: User impersonation!! if (ret == IDOK) - SaveShutdownSelState(pgContext->nShutdownAction); + SaveShutdownSelState(???, pgContext->nShutdownAction); #endif return ret; @@ -1218,17 +1277,17 @@ ShutdownDialog( * - When the Help button is pushed, it sends the same return value as IDCANCEL (0x00), but * at the same time, it calls the help file directly from the dialog box. * - When the dialog is created, it doesn't disable all other input from the other windows. - * This is done elsewhere. When running the function ShellShutdownDialog() from XP/2K3, if the user clicks - * out of the window, it automatically closes itself. - * - The parameter, lpUsername never seems to be used when calling the function from Windows XP. Either + * This is done elsewhere. When running the function ShellShutdownDialog() from XP/2K3, if the user + * clicks out of the window, it automatically closes itself. + * - The parameter pUserName, never seems to be used when calling the function from Windows XP. Either * it was a parameter that was never used in the final version before release, or it has a use that * is currently not known. */ DWORD WINAPI ShellShutdownDialog( - HWND hParent, - LPWSTR lpUsername, - BOOL bHideLogoff) + _In_ HWND hWndParent, + _In_opt_ LPCWSTR pUserName, + _In_ DWORD dwExcludeOptions) { INT_PTR dlgValue; DWORD ShutdownOptions; @@ -1238,31 +1297,62 @@ ShellShutdownDialog( * the cached GINA context but use a local copy here. */ GINA_CONTEXT gContext = {0}; - DWORD BufferSize; - UNREFERENCED_PARAMETER(lpUsername); - - ShutdownOptions = GetAllowedShutdownOptions(); - if (bHideLogoff) - ShutdownOptions &= ~WLX_SHUTDOWN_STATE_LOGOFF; + ShutdownOptions = GetAllowedShutdownOptions(HKEY_CURRENT_USER, NULL); + ShutdownOptions &= ~dwExcludeOptions; /* Initialize our local GINA context */ gContext.hDllInstance = hDllInstance; - BufferSize = _countof(gContext.UserName); - // NOTE: Only when this function is called, Win checks inside - // HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", - // value "Logon User Name", and determines whether it will display - // the user name. - GetUserNameW(gContext.UserName, &BufferSize); - gContext.nShutdownAction = LoadShutdownSelState(); + gContext.nShutdownAction = LoadShutdownSelState(HKEY_CURRENT_USER); + if (gContext.nShutdownAction == 0) + gContext.nShutdownAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; + + if (pUserName) + { + /* Use the caller-given user name */ + StringCbCopyW(gContext.UserName, sizeof(gContext.UserName), pUserName); + } + else + { + /* Use either the convenience "Logon User Name" registered by + * WlxActivateUserShell(), or if not found, retrieve a user name + * via GetUserNameW() */ + HKEY hKey; + LONG lRet; + lRet = RegOpenKeyExW(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", + 0, + KEY_QUERY_VALUE, &hKey); + if (lRet == ERROR_SUCCESS) + { + DWORD dwType, cbData = sizeof(gContext.UserName); + lRet = RegQueryValueExW(hKey, + L"Logon User Name", + NULL, + &dwType, + (PBYTE)gContext.UserName, + &cbData); + RegCloseKey(hKey); + + /* NULL-terminate the string */ + if (lRet != ERROR_SUCCESS) + cbData = 0; + gContext.UserName[cbData / sizeof(WCHAR)] = UNICODE_NULL; + } + else + { + DWORD BufferSize = _countof(gContext.UserName); + GetUserNameW(gContext.UserName, &BufferSize); + } + } /* Load the shutdown dialog box */ - dlgValue = ShutdownDialog(hParent, ShutdownOptions, &gContext); + dlgValue = ShutdownDialog(hWndParent, ShutdownOptions, &gContext); /* Determine what to do based on user selection */ if (dlgValue == IDOK) { - SaveShutdownSelState(gContext.nShutdownAction); + SaveShutdownSelState(HKEY_CURRENT_USER, gContext.nShutdownAction); switch (gContext.nShutdownAction) { @@ -1280,19 +1370,20 @@ ShellShutdownDialog( case WLX_SAS_ACTION_SHUTDOWN_SLEEP: return WLX_SHUTDOWN_STATE_SLEEP; - // 0x20 + case WLX_SAS_ACTION_SHUTDOWN_SLEEP2: + return WLX_SHUTDOWN_STATE_SLEEP2; case WLX_SAS_ACTION_SHUTDOWN_HIBERNATE: return WLX_SHUTDOWN_STATE_HIBERNATE; - // 0x80 + // WLX_SHUTDOWN_STATE_DISCONNECT } } /* Help file is called directly here */ else if (dlgValue == IDHELP) { FIXME("Help is not implemented yet.\n"); - MessageBoxW(hParent, L"Help is not implemented yet.", L"Message", MB_OK | MB_ICONEXCLAMATION); + MessageBoxW(hWndParent, L"Help is not implemented yet.", L"Message", MB_OK | MB_ICONEXCLAMATION); } else if (dlgValue == -1) { @@ -1310,5 +1401,5 @@ ShellShutdownDialog( DWORD WINAPI ShellTurnOffDialog(HWND hWnd) { - return ShellShutdownDialog(hWnd, NULL, FALSE); + return ShellShutdownDialog(hWnd, NULL, 0); } diff --git a/dll/win32/msgina/utils.c b/dll/win32/msgina/utils.c index c53a4c0eaab..fd9a0b7089c 100644 --- a/dll/win32/msgina/utils.c +++ b/dll/win32/msgina/utils.c @@ -108,6 +108,64 @@ ReadRegDwordValue( return ERROR_SUCCESS; } +/** + * @brief + * Verifies whether the specified token has the given privilege. + * + * @see + * shell32!SHTestTokenPrivilegeW(), + * http://undoc.airesoft.co.uk/shell32.dll/SHTestTokenPrivilegeW.php + * and setupapi!DoesUserHavePrivilege(). + */ +BOOL +TestTokenPrivilege( + _In_opt_ HANDLE hToken, + _In_ ULONG Privilege) +{ + LUID PrivilegeLuid = {Privilege, 0}; + HANDLE hNewToken = NULL; + PTOKEN_PRIVILEGES pTokenPriv; + DWORD dwLength; + BOOL ret = FALSE; + + if (!hToken) + { + /* Open effective token */ + ret = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hNewToken); + if (!ret && (GetLastError() == ERROR_NO_TOKEN)) + ret = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hNewToken); + if (!ret || !hNewToken) + return FALSE; + hToken = hNewToken; + } + + dwLength = 0; + ret = GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwLength); + if (!ret && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) + goto Quit; + + ret = FALSE; + pTokenPriv = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwLength); + if (!pTokenPriv) + goto Quit; + + if (GetTokenInformation(hToken, TokenPrivileges, pTokenPriv, dwLength, &dwLength)) + { + DWORD i, cPrivs = pTokenPriv->PrivilegeCount; + for (i = 0; !ret && i < cPrivs; ++i) + { + ret = RtlEqualLuid(&PrivilegeLuid, &pTokenPriv->Privileges[i].Luid); + } + } + + LocalFree(pTokenPriv); + +Quit: + if (hToken == hNewToken) + CloseHandle(hNewToken); + return ret; +} + PWSTR DuplicateString( _In_opt_ PCWSTR Str) diff --git a/dll/win32/shell32/dialogs/dialogs.cpp b/dll/win32/shell32/dialogs/dialogs.cpp index 6c7562228c2..b0ef550df28 100644 --- a/dll/win32/shell32/dialogs/dialogs.cpp +++ b/dll/win32/shell32/dialogs/dialogs.cpp @@ -1603,7 +1603,7 @@ VOID ExitWindowsDialog_backup(HWND hWndOwner) */ void WINAPI ExitWindowsDialog(HWND hWndOwner) { - typedef DWORD (WINAPI *ShellShFunc)(HWND hParent, WCHAR *Username, BOOL bHideLogoff); + typedef DWORD (WINAPI *ShellShFunc)(HWND hWndParent, LPCWSTR pUserName, DWORD dwExcludeOptions); HINSTANCE msginaDll = LoadLibraryW(L"msgina.dll"); TRACE("(%p)\n", hWndOwner); @@ -1623,11 +1623,10 @@ void WINAPI ExitWindowsDialog(HWND hWndOwner) } ShellShFunc pShellShutdownDialog = (ShellShFunc)GetProcAddress(msginaDll, "ShellShutdownDialog"); - if (pShellShutdownDialog) { /* Actually call the function */ - DWORD returnValue = pShellShutdownDialog(parent, NULL, FALSE); + DWORD returnValue = pShellShutdownDialog(parent, NULL, 0); switch (returnValue) {