xref: /reactos/win32ss/user/ntuser/shutdown.c (revision 90ed6862)
1c2c66affSColin Finck /*
2c2c66affSColin Finck  * COPYRIGHT:        See COPYING in the top level directory
3c2c66affSColin Finck  * PROJECT:          ReactOS Win32k subsystem
4c2c66affSColin Finck  * PURPOSE:          Shutdown routines
5c2c66affSColin Finck  * FILE:             win32ss/user/ntuser/shutdown.c
6c2c66affSColin Finck  * PROGRAMER:        Hermes Belusca
7c2c66affSColin Finck  */
8c2c66affSColin Finck 
9c2c66affSColin Finck #include <win32k.h>
10c2c66affSColin Finck 
11c2c66affSColin Finck DBG_DEFAULT_CHANNEL(UserShutdown);
12c2c66affSColin Finck 
13c2c66affSColin Finck /* Our local copy of shutdown flags */
14c2c66affSColin Finck static ULONG gdwShutdownFlags = 0;
15c2c66affSColin Finck 
16c2c66affSColin Finck /*
17c2c66affSColin Finck  * Based on CSRSS and described in pages 1115 - 1118 "Windows Internals, Fifth Edition".
18c2c66affSColin Finck  * CSRSS sends WM_CLIENTSHUTDOWN messages to top-level windows, and it is our job
19c2c66affSColin Finck  * to send WM_QUERYENDSESSION / WM_ENDSESSION messages in response.
20c2c66affSColin Finck  */
21c2c66affSColin Finck LRESULT
IntClientShutdown(IN PWND pWindow,IN WPARAM wParam,IN LPARAM lParam)22c2c66affSColin Finck IntClientShutdown(IN PWND pWindow,
23c2c66affSColin Finck                   IN WPARAM wParam,
24c2c66affSColin Finck                   IN LPARAM lParam)
25c2c66affSColin Finck {
26c2c66affSColin Finck     LPARAM lParams;
27c2c66affSColin Finck     BOOL KillTimers;
28c2c66affSColin Finck     INT i;
29c2c66affSColin Finck     LRESULT lResult = MCSR_GOODFORSHUTDOWN;
30c2c66affSColin Finck     HWND *List;
31c2c66affSColin Finck 
32c2c66affSColin Finck     KillTimers = wParam & MCS_ENDSESSION ? TRUE : FALSE;
33c2c66affSColin Finck     lParams = lParam & (ENDSESSION_LOGOFF | ENDSESSION_CRITICAL | ENDSESSION_CLOSEAPP);
34c2c66affSColin Finck 
35c2c66affSColin Finck     /* First, send end sessions to children */
36c2c66affSColin Finck     List = IntWinListChildren(pWindow);
37c2c66affSColin Finck 
38c2c66affSColin Finck     if (List)
39c2c66affSColin Finck     {
40c2c66affSColin Finck         for (i = 0; List[i]; i++)
41c2c66affSColin Finck         {
42c2c66affSColin Finck             PWND WndChild;
43c2c66affSColin Finck 
44c2c66affSColin Finck             if (!(WndChild = UserGetWindowObject(List[i])))
45c2c66affSColin Finck                 continue;
46c2c66affSColin Finck 
47c2c66affSColin Finck             if (wParam & MCS_QUERYENDSESSION)
48c2c66affSColin Finck             {
49*90ed6862SThamatip Chitpong                 if (!co_IntSendMessage(UserHMGetHandle(WndChild), WM_QUERYENDSESSION, 0, lParams))
50c2c66affSColin Finck                 {
51c2c66affSColin Finck                     lResult = MCSR_DONOTSHUTDOWN;
52c2c66affSColin Finck                     break;
53c2c66affSColin Finck                 }
54c2c66affSColin Finck             }
55c2c66affSColin Finck             else
56c2c66affSColin Finck             {
57*90ed6862SThamatip Chitpong                 co_IntSendMessage(UserHMGetHandle(WndChild), WM_ENDSESSION, KillTimers, lParams);
58c2c66affSColin Finck                 if (KillTimers)
59c2c66affSColin Finck                 {
60c2c66affSColin Finck                     DestroyTimersForWindow(WndChild->head.pti, WndChild);
61c2c66affSColin Finck                 }
62c2c66affSColin Finck                 lResult = MCSR_SHUTDOWNFINISHED;
63c2c66affSColin Finck             }
64c2c66affSColin Finck         }
65c2c66affSColin Finck         ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
66c2c66affSColin Finck         if (lResult == MCSR_DONOTSHUTDOWN)
67c2c66affSColin Finck             return lResult;
68c2c66affSColin Finck     }
69c2c66affSColin Finck 
70c2c66affSColin Finck     /* Send to the caller */
71c2c66affSColin Finck     if (wParam & MCS_QUERYENDSESSION)
72c2c66affSColin Finck     {
73*90ed6862SThamatip Chitpong         if (!co_IntSendMessage(UserHMGetHandle(pWindow), WM_QUERYENDSESSION, 0, lParams))
74c2c66affSColin Finck         {
75c2c66affSColin Finck             lResult = MCSR_DONOTSHUTDOWN;
76c2c66affSColin Finck         }
77c2c66affSColin Finck     }
78c2c66affSColin Finck     else
79c2c66affSColin Finck     {
80*90ed6862SThamatip Chitpong         co_IntSendMessage(UserHMGetHandle(pWindow), WM_ENDSESSION, KillTimers, lParams);
81c2c66affSColin Finck         if (KillTimers)
82c2c66affSColin Finck         {
83c2c66affSColin Finck             DestroyTimersForWindow(pWindow->head.pti, pWindow);
84c2c66affSColin Finck         }
85c2c66affSColin Finck         lResult = MCSR_SHUTDOWNFINISHED;
86c2c66affSColin Finck     }
87c2c66affSColin Finck 
88c2c66affSColin Finck     return lResult;
89c2c66affSColin Finck }
90c2c66affSColin Finck 
91c2c66affSColin Finck BOOLEAN
HasPrivilege(IN PPRIVILEGE_SET Privilege)92c2c66affSColin Finck HasPrivilege(IN PPRIVILEGE_SET Privilege)
93c2c66affSColin Finck {
94c2c66affSColin Finck     BOOLEAN Result;
95c2c66affSColin Finck     SECURITY_SUBJECT_CONTEXT SubjectContext;
96c2c66affSColin Finck 
97c2c66affSColin Finck     /* Capture and lock the security subject context */
98c2c66affSColin Finck     SeCaptureSubjectContext(&SubjectContext);
99c2c66affSColin Finck     SeLockSubjectContext(&SubjectContext);
100c2c66affSColin Finck 
101c2c66affSColin Finck     /* Do privilege check */
102c2c66affSColin Finck     Result = SePrivilegeCheck(Privilege, &SubjectContext, UserMode);
103c2c66affSColin Finck 
104c2c66affSColin Finck     /* Audit the privilege */
105c2c66affSColin Finck #if 0
106c2c66affSColin Finck     SePrivilegeObjectAuditAlarm(NULL,
107c2c66affSColin Finck                                 &SubjectContext,
108c2c66affSColin Finck                                 0,
109c2c66affSColin Finck                                 Privilege,
110c2c66affSColin Finck                                 Result,
111c2c66affSColin Finck                                 UserMode);
112c2c66affSColin Finck #endif
113c2c66affSColin Finck 
114c2c66affSColin Finck     /* Unlock and release the security subject context and return */
115c2c66affSColin Finck     SeUnlockSubjectContext(&SubjectContext);
116c2c66affSColin Finck     SeReleaseSubjectContext(&SubjectContext);
117c2c66affSColin Finck     return Result;
118c2c66affSColin Finck }
119c2c66affSColin Finck 
120c2c66affSColin Finck BOOL
NotifyLogon(IN HWND hWndSta,IN PLUID CallerLuid,IN ULONG Flags,IN NTSTATUS ShutdownStatus)121c2c66affSColin Finck NotifyLogon(IN HWND hWndSta,
122c2c66affSColin Finck             IN PLUID CallerLuid,
123c2c66affSColin Finck             IN ULONG Flags,
124c2c66affSColin Finck             IN NTSTATUS ShutdownStatus)
125c2c66affSColin Finck {
126c2c66affSColin Finck     // LUID SystemLuid = SYSTEM_LUID;
127c2c66affSColin Finck     ULONG Notif, Param;
128c2c66affSColin Finck 
129c2c66affSColin Finck     ERR("NotifyLogon(0x%lx, 0x%lx)\n", Flags, ShutdownStatus);
130c2c66affSColin Finck 
131c2c66affSColin Finck     /* If no Winlogon notifications are needed, just return */
132c2c66affSColin Finck     if (Flags & EWX_NONOTIFY)
133c2c66affSColin Finck         return FALSE;
134c2c66affSColin Finck 
135c2c66affSColin Finck     /* In case we cancelled the shutdown...*/
136c2c66affSColin Finck     if (Flags & EWX_SHUTDOWN_CANCELED)
137c2c66affSColin Finck     {
138c2c66affSColin Finck         /* ... send a LN_LOGOFF_CANCELED to Winlogon with the real cancel status... */
139c2c66affSColin Finck         Notif = LN_LOGOFF_CANCELED;
140c2c66affSColin Finck         Param = ShutdownStatus;
141c2c66affSColin Finck     }
142c2c66affSColin Finck     else
143c2c66affSColin Finck     {
144c2c66affSColin Finck         /* ... otherwise it's a real logoff. Send the shutdown flags in that case. */
145c2c66affSColin Finck         Notif = LN_LOGOFF;
146c2c66affSColin Finck         Param = Flags;
147c2c66affSColin Finck     }
148c2c66affSColin Finck 
149c2c66affSColin Finck     // FIXME: At the moment, always send the notifications... In real world some checks are done.
150c2c66affSColin Finck     // if (hwndSAS && ( (Flags & EWX_SHUTDOWN) || RtlEqualLuid(CallerLuid, &SystemLuid)) )
151c2c66affSColin Finck     if (hwndSAS)
152c2c66affSColin Finck     {
153c2c66affSColin Finck         TRACE("\tSending %s message to Winlogon\n", Notif == LN_LOGOFF ? "LN_LOGOFF" : "LN_LOGOFF_CANCELED");
154c2c66affSColin Finck         UserPostMessage(hwndSAS, WM_LOGONNOTIFY, Notif, (LPARAM)Param);
155c2c66affSColin Finck         return TRUE;
156c2c66affSColin Finck     }
157c2c66affSColin Finck     else
158c2c66affSColin Finck     {
159c2c66affSColin Finck         ERR("hwndSAS == NULL\n");
160c2c66affSColin Finck     }
161c2c66affSColin Finck 
162c2c66affSColin Finck     return FALSE;
163c2c66affSColin Finck }
164c2c66affSColin Finck 
165c2c66affSColin Finck NTSTATUS
UserInitiateShutdown(IN PETHREAD Thread,IN OUT PULONG pFlags)166c2c66affSColin Finck UserInitiateShutdown(IN PETHREAD Thread,
167c2c66affSColin Finck                      IN OUT PULONG pFlags)
168c2c66affSColin Finck {
169c2c66affSColin Finck     NTSTATUS Status;
170c2c66affSColin Finck     ULONG Flags = *pFlags;
171c2c66affSColin Finck     LUID CallerLuid;
172c2c66affSColin Finck     LUID SystemLuid = SYSTEM_LUID;
173c2c66affSColin Finck     static PRIVILEGE_SET ShutdownPrivilege =
174c2c66affSColin Finck     {
175c2c66affSColin Finck         1, PRIVILEGE_SET_ALL_NECESSARY,
176c2c66affSColin Finck         { {{SE_SHUTDOWN_PRIVILEGE, 0}, 0} }
177c2c66affSColin Finck     };
178c2c66affSColin Finck 
179c2c66affSColin Finck     PPROCESSINFO ppi;
180c2c66affSColin Finck 
181c2c66affSColin Finck     TRACE("UserInitiateShutdown\n");
182c2c66affSColin Finck 
183c2c66affSColin Finck     /* Get the caller's LUID */
1842345d63cSHermès Bélusca-Maïto     Status = GetProcessLuid(Thread, NULL, &CallerLuid);
185c2c66affSColin Finck     if (!NT_SUCCESS(Status))
186c2c66affSColin Finck     {
187c2c66affSColin Finck         ERR("UserInitiateShutdown: GetProcessLuid failed\n");
188c2c66affSColin Finck         return Status;
189c2c66affSColin Finck     }
190c2c66affSColin Finck 
191c2c66affSColin Finck     /*
192c2c66affSColin Finck      * Check if this is the System LUID, and adjust flags if needed.
193c2c66affSColin Finck      * In particular, be sure there is no EWX_CALLER_SYSTEM flag
194c2c66affSColin Finck      * spuriously set (could be the sign of malicous app!).
195c2c66affSColin Finck      */
196c2c66affSColin Finck     if (RtlEqualLuid(&CallerLuid, &SystemLuid))
197c2c66affSColin Finck         Flags |= EWX_CALLER_SYSTEM;
198c2c66affSColin Finck     else
199c2c66affSColin Finck         Flags &= ~EWX_CALLER_SYSTEM;
200c2c66affSColin Finck 
201c2c66affSColin Finck     *pFlags = Flags;
202c2c66affSColin Finck 
203c2c66affSColin Finck     /* Retrieve the Win32 process info */
204c2c66affSColin Finck     ppi = PsGetProcessWin32Process(PsGetThreadProcess(Thread));
205c2c66affSColin Finck     if (ppi == NULL)
206c2c66affSColin Finck     {
207c2c66affSColin Finck         ERR("UserInitiateShutdown: Failed to get win32 thread!\n");
208c2c66affSColin Finck         return STATUS_INVALID_HANDLE;
209c2c66affSColin Finck     }
210c2c66affSColin Finck 
211c2c66affSColin Finck     /* If the caller is not Winlogon, do some security checks */
212c2c66affSColin Finck     if (PsGetThreadProcessId(Thread) != gpidLogon)
213c2c66affSColin Finck     {
214c2c66affSColin Finck         /*
215c2c66affSColin Finck          * Here also, be sure there is no EWX_CALLER_WINLOGON flag
216c2c66affSColin Finck          * spuriously set (could be the sign of malicous app!).
217c2c66affSColin Finck          */
218c2c66affSColin Finck         Flags &= ~EWX_CALLER_WINLOGON;
219c2c66affSColin Finck 
220c2c66affSColin Finck         *pFlags = Flags;
221c2c66affSColin Finck 
222c2c66affSColin Finck         /* Check whether the current process is attached to a window station */
223c2c66affSColin Finck         if (ppi->prpwinsta == NULL)
224c2c66affSColin Finck         {
225c2c66affSColin Finck             ERR("UserInitiateShutdown: Process is not attached to a desktop\n");
226c2c66affSColin Finck             return STATUS_INVALID_HANDLE;
227c2c66affSColin Finck         }
228c2c66affSColin Finck 
229c2c66affSColin Finck         /* Check whether the window station of the current process can send exit requests */
230c2c66affSColin Finck         if (!RtlAreAllAccessesGranted(ppi->amwinsta, WINSTA_EXITWINDOWS))
231c2c66affSColin Finck         {
232c2c66affSColin Finck             ERR("UserInitiateShutdown: Caller doesn't have the rights to shutdown\n");
233c2c66affSColin Finck             return STATUS_ACCESS_DENIED;
234c2c66affSColin Finck         }
235c2c66affSColin Finck 
236c2c66affSColin Finck         /*
237c2c66affSColin Finck          * NOTE: USERSRV automatically adds the shutdown flag when we poweroff or reboot.
238c2c66affSColin Finck          *
239c2c66affSColin Finck          * If the caller wants to shutdown / reboot / power-off...
240c2c66affSColin Finck          */
241c2c66affSColin Finck         if (Flags & EWX_SHUTDOWN)
242c2c66affSColin Finck         {
243c2c66affSColin Finck             /* ... check whether it has shutdown privilege */
244c2c66affSColin Finck             if (!HasPrivilege(&ShutdownPrivilege))
245c2c66affSColin Finck             {
246c2c66affSColin Finck                 ERR("UserInitiateShutdown: Caller doesn't have the rights to shutdown\n");
247c2c66affSColin Finck                 return STATUS_PRIVILEGE_NOT_HELD;
248c2c66affSColin Finck             }
249c2c66affSColin Finck         }
250c2c66affSColin Finck         else
251c2c66affSColin Finck         {
252c2c66affSColin Finck             /*
253c2c66affSColin Finck              * ... but if it just wants to log-off, in case its
254c2c66affSColin Finck              * window station is a non-IO one, fail the call.
255c2c66affSColin Finck              */
256c2c66affSColin Finck             if (ppi->prpwinsta->Flags & WSS_NOIO)
257c2c66affSColin Finck             {
258c2c66affSColin Finck                 ERR("UserInitiateShutdown: Caller doesn't have the rights to logoff\n");
259c2c66affSColin Finck                 return STATUS_INVALID_DEVICE_REQUEST;
260c2c66affSColin Finck             }
261c2c66affSColin Finck         }
262c2c66affSColin Finck     }
263c2c66affSColin Finck 
264c2c66affSColin Finck     /* If the caller is not Winlogon, possibly notify it to perform the real shutdown */
265c2c66affSColin Finck     if (PsGetThreadProcessId(Thread) != gpidLogon)
266c2c66affSColin Finck     {
267c2c66affSColin Finck         // FIXME: HACK!! Do more checks!!
268c2c66affSColin Finck         TRACE("UserInitiateShutdown: Notify Winlogon for shutdown\n");
269c2c66affSColin Finck         NotifyLogon(hwndSAS, &CallerLuid, Flags, STATUS_SUCCESS);
270c2c66affSColin Finck         return STATUS_PENDING;
271c2c66affSColin Finck     }
272c2c66affSColin Finck 
273c2c66affSColin Finck     // If we reach this point, that means it's Winlogon that triggered the shutdown.
274c2c66affSColin Finck     TRACE("UserInitiateShutdown: Winlogon is doing a shutdown\n");
275c2c66affSColin Finck 
276c2c66affSColin Finck     /*
277c2c66affSColin Finck      * Update and save the shutdown flags globally for renotifying
278c2c66affSColin Finck      * Winlogon if needed, when calling EndShutdown.
279c2c66affSColin Finck      */
280c2c66affSColin Finck     Flags |= EWX_CALLER_WINLOGON; // Winlogon is doing a shutdown, be sure the internal flag is set.
281c2c66affSColin Finck     *pFlags = Flags;
282c2c66affSColin Finck 
283c2c66affSColin Finck     /* Save the shutdown flags now */
284c2c66affSColin Finck     gdwShutdownFlags = Flags;
285c2c66affSColin Finck 
286c2c66affSColin Finck     return STATUS_SUCCESS;
287c2c66affSColin Finck }
288c2c66affSColin Finck 
289c2c66affSColin Finck NTSTATUS
UserEndShutdown(IN PETHREAD Thread,IN NTSTATUS ShutdownStatus)290c2c66affSColin Finck UserEndShutdown(IN PETHREAD Thread,
291c2c66affSColin Finck                 IN NTSTATUS ShutdownStatus)
292c2c66affSColin Finck {
293c2c66affSColin Finck     NTSTATUS Status;
294c2c66affSColin Finck     ULONG Flags;
295c2c66affSColin Finck     LUID CallerLuid;
296c2c66affSColin Finck 
297c2c66affSColin Finck     TRACE("UserEndShutdown called\n");
298c2c66affSColin Finck 
299c2c66affSColin Finck     /*
300c2c66affSColin Finck      * FIXME: Some cleanup should be done when shutdown succeeds,
301c2c66affSColin Finck      * and some reset should be done when shutdown is cancelled.
302c2c66affSColin Finck      */
303c2c66affSColin Finck     //STUB;
304c2c66affSColin Finck 
3052345d63cSHermès Bélusca-Maïto     Status = GetProcessLuid(Thread, NULL, &CallerLuid);
306c2c66affSColin Finck     if (!NT_SUCCESS(Status))
307c2c66affSColin Finck     {
3082345d63cSHermès Bélusca-Maïto         ERR("UserEndShutdown: GetProcessLuid failed\n");
309c2c66affSColin Finck         return Status;
310c2c66affSColin Finck     }
311c2c66affSColin Finck 
312c2c66affSColin Finck     /* Copy the global flags because we're going to modify them for our purposes */
313c2c66affSColin Finck     Flags = gdwShutdownFlags;
314c2c66affSColin Finck 
315c2c66affSColin Finck     if (NT_SUCCESS(ShutdownStatus))
316c2c66affSColin Finck     {
317c2c66affSColin Finck         /* Just report success, and keep the shutdown flags as they are */
318c2c66affSColin Finck         ShutdownStatus = STATUS_SUCCESS;
319c2c66affSColin Finck     }
320c2c66affSColin Finck     else
321c2c66affSColin Finck     {
322c2c66affSColin Finck         /* Report the status to Winlogon and say that we cancel the shutdown */
323c2c66affSColin Finck         Flags |= EWX_SHUTDOWN_CANCELED;
324c2c66affSColin Finck         // FIXME: Should we reset gdwShutdownFlags to 0 ??
325c2c66affSColin Finck     }
326c2c66affSColin Finck 
327c2c66affSColin Finck     TRACE("UserEndShutdown: Notify Winlogon for end of shutdown\n");
328c2c66affSColin Finck     NotifyLogon(hwndSAS, &CallerLuid, Flags, ShutdownStatus);
329c2c66affSColin Finck 
330c2c66affSColin Finck     /* Always return success */
331c2c66affSColin Finck     return STATUS_SUCCESS;
332c2c66affSColin Finck }
333c2c66affSColin Finck 
334c2c66affSColin Finck /* EOF */
335