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