+++ /dev/null
-/*
- * COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS Win32k subsystem
- * PURPOSE: Shutdown routines
- * FILE: win32ss/user/ntuser/shutdown.c
- * PROGRAMER: Hermes Belusca
- */
-
-#include <win32k.h>
-
-DBG_DEFAULT_CHANNEL(UserShutdown);
-
-/* Our local copy of shutdown flags */
-static ULONG gdwShutdownFlags = 0;
-
-/*
- * Based on CSRSS and described in pages 1115 - 1118 "Windows Internals, Fifth Edition".
- * CSRSS sends WM_CLIENTSHUTDOWN messages to top-level windows, and it is our job
- * to send WM_QUERYENDSESSION / WM_ENDSESSION messages in response.
- */
-LRESULT
-IntClientShutdown(IN PWND pWindow,
- IN WPARAM wParam,
- IN LPARAM lParam)
-{
- LPARAM lParams;
- BOOL KillTimers;
- INT i;
- LRESULT lResult = MCSR_GOODFORSHUTDOWN;
- HWND *List;
-
- KillTimers = wParam & MCS_ENDSESSION ? TRUE : FALSE;
- lParams = lParam & (ENDSESSION_LOGOFF | ENDSESSION_CRITICAL | ENDSESSION_CLOSEAPP);
-
- /* First, send end sessions to children */
- List = IntWinListChildren(pWindow);
-
- if (List)
- {
- for (i = 0; List[i]; i++)
- {
- PWND WndChild;
-
- if (!(WndChild = UserGetWindowObject(List[i])))
- continue;
-
- if (wParam & MCS_QUERYENDSESSION)
- {
- if (!co_IntSendMessage(WndChild->head.h, WM_QUERYENDSESSION, 0, lParams))
- {
- lResult = MCSR_DONOTSHUTDOWN;
- break;
- }
- }
- else
- {
- co_IntSendMessage(WndChild->head.h, WM_ENDSESSION, KillTimers, lParams);
- if (KillTimers)
- {
- DestroyTimersForWindow(WndChild->head.pti, WndChild);
- }
- lResult = MCSR_SHUTDOWNFINISHED;
- }
- }
- ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
- if (lResult == MCSR_DONOTSHUTDOWN)
- return lResult;
- }
-
- /* Send to the caller */
- if (wParam & MCS_QUERYENDSESSION)
- {
- if (!co_IntSendMessage(pWindow->head.h, WM_QUERYENDSESSION, 0, lParams))
- {
- lResult = MCSR_DONOTSHUTDOWN;
- }
- }
- else
- {
- co_IntSendMessage(pWindow->head.h, WM_ENDSESSION, KillTimers, lParams);
- if (KillTimers)
- {
- DestroyTimersForWindow(pWindow->head.pti, pWindow);
- }
- lResult = MCSR_SHUTDOWNFINISHED;
- }
-
- return lResult;
-}
-
-
-NTSTATUS
-GetProcessLuid(IN PETHREAD Thread OPTIONAL,
- OUT PLUID Luid)
-{
- NTSTATUS Status;
- PACCESS_TOKEN Token;
- SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
- BOOLEAN CopyOnOpen, EffectiveOnly;
-
- if (Thread == NULL)
- Thread = PsGetCurrentThread();
-
- /* Use a thread token */
- Token = PsReferenceImpersonationToken(Thread,
- &CopyOnOpen,
- &EffectiveOnly,
- &ImpersonationLevel);
- if (Token == NULL)
- {
- /* We don't have a thread token, use a process token */
- Token = PsReferencePrimaryToken(PsGetThreadProcess(Thread));
-
- /* If no token, fail */
- if (Token == NULL)
- return STATUS_NO_TOKEN;
- }
-
- /* Query the LUID */
- Status = SeQueryAuthenticationIdToken(Token, Luid);
-
- /* Get rid of the token and return */
- ObDereferenceObject(Token);
- return Status;
-}
-
-BOOLEAN
-HasPrivilege(IN PPRIVILEGE_SET Privilege)
-{
- BOOLEAN Result;
- SECURITY_SUBJECT_CONTEXT SubjectContext;
-
- /* Capture and lock the security subject context */
- SeCaptureSubjectContext(&SubjectContext);
- SeLockSubjectContext(&SubjectContext);
-
- /* Do privilege check */
- Result = SePrivilegeCheck(Privilege, &SubjectContext, UserMode);
-
- /* Audit the privilege */
-#if 0
- SePrivilegeObjectAuditAlarm(NULL,
- &SubjectContext,
- 0,
- Privilege,
- Result,
- UserMode);
-#endif
-
- /* Unlock and release the security subject context and return */
- SeUnlockSubjectContext(&SubjectContext);
- SeReleaseSubjectContext(&SubjectContext);
- return Result;
-}
-
-BOOL
-NotifyLogon(IN HWND hWndSta,
- IN PLUID CallerLuid,
- IN ULONG Flags,
- IN NTSTATUS ShutdownStatus)
-{
- // LUID SystemLuid = SYSTEM_LUID;
- ULONG Notif, Param;
-
- ERR("NotifyLogon(0x%lx, 0x%lx)\n", Flags, ShutdownStatus);
-
- /* If no Winlogon notifications are needed, just return */
- if (Flags & EWX_NONOTIFY)
- return FALSE;
-
- /* In case we cancelled the shutdown...*/
- if (Flags & EWX_SHUTDOWN_CANCELED)
- {
- /* ... send a LN_LOGOFF_CANCELED to Winlogon with the real cancel status... */
- Notif = LN_LOGOFF_CANCELED;
- Param = ShutdownStatus;
- }
- else
- {
- /* ... otherwise it's a real logoff. Send the shutdown flags in that case. */
- Notif = LN_LOGOFF;
- Param = Flags;
- }
-
- // FIXME: At the moment, always send the notifications... In real world some checks are done.
- // if (hwndSAS && ( (Flags & EWX_SHUTDOWN) || RtlEqualLuid(CallerLuid, &SystemLuid)) )
- if (hwndSAS)
- {
- TRACE("\tSending %s message to Winlogon\n", Notif == LN_LOGOFF ? "LN_LOGOFF" : "LN_LOGOFF_CANCELED");
- UserPostMessage(hwndSAS, WM_LOGONNOTIFY, Notif, (LPARAM)Param);
- return TRUE;
- }
- else
- {
- ERR("hwndSAS == NULL\n");
- }
-
- return FALSE;
-}
-
-NTSTATUS
-UserInitiateShutdown(IN PETHREAD Thread,
- IN OUT PULONG pFlags)
-{
- NTSTATUS Status;
- ULONG Flags = *pFlags;
- LUID CallerLuid;
- LUID SystemLuid = SYSTEM_LUID;
- static PRIVILEGE_SET ShutdownPrivilege =
- {
- 1, PRIVILEGE_SET_ALL_NECESSARY,
- { {{SE_SHUTDOWN_PRIVILEGE, 0}, 0} }
- };
-
- PPROCESSINFO ppi;
-
- TRACE("UserInitiateShutdown\n");
-
- /* Get the caller's LUID */
- Status = GetProcessLuid(Thread, &CallerLuid);
- if (!NT_SUCCESS(Status))
- {
- ERR("UserInitiateShutdown: GetProcessLuid failed\n");
- return Status;
- }
-
- /*
- * Check if this is the System LUID, and adjust flags if needed.
- * In particular, be sure there is no EWX_CALLER_SYSTEM flag
- * spuriously set (could be the sign of malicous app!).
- */
- if (RtlEqualLuid(&CallerLuid, &SystemLuid))
- Flags |= EWX_CALLER_SYSTEM;
- else
- Flags &= ~EWX_CALLER_SYSTEM;
-
- *pFlags = Flags;
-
- /* Retrieve the Win32 process info */
- ppi = PsGetProcessWin32Process(PsGetThreadProcess(Thread));
- if (ppi == NULL)
- {
- ERR("UserInitiateShutdown: Failed to get win32 thread!\n");
- return STATUS_INVALID_HANDLE;
- }
-
- /* If the caller is not Winlogon, do some security checks */
- if (PsGetThreadProcessId(Thread) != gpidLogon)
- {
- /*
- * Here also, be sure there is no EWX_CALLER_WINLOGON flag
- * spuriously set (could be the sign of malicous app!).
- */
- Flags &= ~EWX_CALLER_WINLOGON;
-
- *pFlags = Flags;
-
- /* Check whether the current process is attached to a window station */
- if (ppi->prpwinsta == NULL)
- {
- ERR("UserInitiateShutdown: Process is not attached to a desktop\n");
- return STATUS_INVALID_HANDLE;
- }
-
- /* Check whether the window station of the current process can send exit requests */
- if (!RtlAreAllAccessesGranted(ppi->amwinsta, WINSTA_EXITWINDOWS))
- {
- ERR("UserInitiateShutdown: Caller doesn't have the rights to shutdown\n");
- return STATUS_ACCESS_DENIED;
- }
-
- /*
- * NOTE: USERSRV automatically adds the shutdown flag when we poweroff or reboot.
- *
- * If the caller wants to shutdown / reboot / power-off...
- */
- if (Flags & EWX_SHUTDOWN)
- {
- /* ... check whether it has shutdown privilege */
- if (!HasPrivilege(&ShutdownPrivilege))
- {
- ERR("UserInitiateShutdown: Caller doesn't have the rights to shutdown\n");
- return STATUS_PRIVILEGE_NOT_HELD;
- }
- }
- else
- {
- /*
- * ... but if it just wants to log-off, in case its
- * window station is a non-IO one, fail the call.
- */
- if (ppi->prpwinsta->Flags & WSS_NOIO)
- {
- ERR("UserInitiateShutdown: Caller doesn't have the rights to logoff\n");
- return STATUS_INVALID_DEVICE_REQUEST;
- }
- }
- }
-
- /* If the caller is not Winlogon, possibly notify it to perform the real shutdown */
- if (PsGetThreadProcessId(Thread) != gpidLogon)
- {
- // FIXME: HACK!! Do more checks!!
- TRACE("UserInitiateShutdown: Notify Winlogon for shutdown\n");
- NotifyLogon(hwndSAS, &CallerLuid, Flags, STATUS_SUCCESS);
- return STATUS_PENDING;
- }
-
- // If we reach this point, that means it's Winlogon that triggered the shutdown.
- TRACE("UserInitiateShutdown: Winlogon is doing a shutdown\n");
-
- /*
- * Update and save the shutdown flags globally for renotifying
- * Winlogon if needed, when calling EndShutdown.
- */
- Flags |= EWX_CALLER_WINLOGON; // Winlogon is doing a shutdown, be sure the internal flag is set.
- *pFlags = Flags;
-
- /* Save the shutdown flags now */
- gdwShutdownFlags = Flags;
-
- return STATUS_SUCCESS;
-}
-
-NTSTATUS
-UserEndShutdown(IN PETHREAD Thread,
- IN NTSTATUS ShutdownStatus)
-{
- NTSTATUS Status;
- ULONG Flags;
- LUID CallerLuid;
-
- TRACE("UserEndShutdown called\n");
-
- /*
- * FIXME: Some cleanup should be done when shutdown succeeds,
- * and some reset should be done when shutdown is cancelled.
- */
- //STUB;
-
- Status = GetProcessLuid(Thread, &CallerLuid);
- if (!NT_SUCCESS(Status))
- {
- ERR("GetProcessLuid failed\n");
- return Status;
- }
-
- /* Copy the global flags because we're going to modify them for our purposes */
- Flags = gdwShutdownFlags;
-
- if (NT_SUCCESS(ShutdownStatus))
- {
- /* Just report success, and keep the shutdown flags as they are */
- ShutdownStatus = STATUS_SUCCESS;
- }
- else
- {
- /* Report the status to Winlogon and say that we cancel the shutdown */
- Flags |= EWX_SHUTDOWN_CANCELED;
- // FIXME: Should we reset gdwShutdownFlags to 0 ??
- }
-
- TRACE("UserEndShutdown: Notify Winlogon for end of shutdown\n");
- NotifyLogon(hwndSAS, &CallerLuid, Flags, ShutdownStatus);
-
- /* Always return success */
- return STATUS_SUCCESS;
-}
-
-/* EOF */