2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Shutdown routines
5 * FILE: win32ss/user/ntuser/shutdown.c
6 * PROGRAMER: Hermes Belusca
11 DBG_DEFAULT_CHANNEL(UserShutdown
);
13 /* Our local copy of shutdown flags */
14 static ULONG gdwShutdownFlags
= 0;
17 * Based on CSRSS and described in pages 1115 - 1118 "Windows Internals, Fifth Edition".
18 * CSRSS sends WM_CLIENTSHUTDOWN messages to top-level windows, and it is our job
19 * to send WM_QUERYENDSESSION / WM_ENDSESSION messages in response.
22 IntClientShutdown(IN PWND pWindow
,
29 LRESULT lResult
= MCSR_GOODFORSHUTDOWN
;
32 KillTimers
= wParam
& MCS_ENDSESSION
? TRUE
: FALSE
;
33 lParams
= lParam
& (ENDSESSION_LOGOFF
| ENDSESSION_CRITICAL
| ENDSESSION_CLOSEAPP
);
35 /* First, send end sessions to children */
36 List
= IntWinListChildren(pWindow
);
40 for (i
= 0; List
[i
]; i
++)
44 if (!(WndChild
= UserGetWindowObject(List
[i
])))
47 if (wParam
& MCS_QUERYENDSESSION
)
49 if (!co_IntSendMessage(WndChild
->head
.h
, WM_QUERYENDSESSION
, 0, lParams
))
51 lResult
= MCSR_DONOTSHUTDOWN
;
57 co_IntSendMessage(WndChild
->head
.h
, WM_ENDSESSION
, KillTimers
, lParams
);
60 DestroyTimersForWindow(WndChild
->head
.pti
, WndChild
);
62 lResult
= MCSR_SHUTDOWNFINISHED
;
65 ExFreePoolWithTag(List
, USERTAG_WINDOWLIST
);
66 if (lResult
== MCSR_DONOTSHUTDOWN
)
70 /* Send to the caller */
71 if (wParam
& MCS_QUERYENDSESSION
)
73 if (!co_IntSendMessage(pWindow
->head
.h
, WM_QUERYENDSESSION
, 0, lParams
))
75 lResult
= MCSR_DONOTSHUTDOWN
;
80 co_IntSendMessage(pWindow
->head
.h
, WM_ENDSESSION
, KillTimers
, lParams
);
83 DestroyTimersForWindow(pWindow
->head
.pti
, pWindow
);
85 lResult
= MCSR_SHUTDOWNFINISHED
;
92 HasPrivilege(IN PPRIVILEGE_SET Privilege
)
95 SECURITY_SUBJECT_CONTEXT SubjectContext
;
97 /* Capture and lock the security subject context */
98 SeCaptureSubjectContext(&SubjectContext
);
99 SeLockSubjectContext(&SubjectContext
);
101 /* Do privilege check */
102 Result
= SePrivilegeCheck(Privilege
, &SubjectContext
, UserMode
);
104 /* Audit the privilege */
106 SePrivilegeObjectAuditAlarm(NULL
,
114 /* Unlock and release the security subject context and return */
115 SeUnlockSubjectContext(&SubjectContext
);
116 SeReleaseSubjectContext(&SubjectContext
);
121 NotifyLogon(IN HWND hWndSta
,
124 IN NTSTATUS ShutdownStatus
)
126 // LUID SystemLuid = SYSTEM_LUID;
129 ERR("NotifyLogon(0x%lx, 0x%lx)\n", Flags
, ShutdownStatus
);
131 /* If no Winlogon notifications are needed, just return */
132 if (Flags
& EWX_NONOTIFY
)
135 /* In case we cancelled the shutdown...*/
136 if (Flags
& EWX_SHUTDOWN_CANCELED
)
138 /* ... send a LN_LOGOFF_CANCELED to Winlogon with the real cancel status... */
139 Notif
= LN_LOGOFF_CANCELED
;
140 Param
= ShutdownStatus
;
144 /* ... otherwise it's a real logoff. Send the shutdown flags in that case. */
149 // FIXME: At the moment, always send the notifications... In real world some checks are done.
150 // if (hwndSAS && ( (Flags & EWX_SHUTDOWN) || RtlEqualLuid(CallerLuid, &SystemLuid)) )
153 TRACE("\tSending %s message to Winlogon\n", Notif
== LN_LOGOFF
? "LN_LOGOFF" : "LN_LOGOFF_CANCELED");
154 UserPostMessage(hwndSAS
, WM_LOGONNOTIFY
, Notif
, (LPARAM
)Param
);
159 ERR("hwndSAS == NULL\n");
166 UserInitiateShutdown(IN PETHREAD Thread
,
167 IN OUT PULONG pFlags
)
170 ULONG Flags
= *pFlags
;
172 LUID SystemLuid
= SYSTEM_LUID
;
173 static PRIVILEGE_SET ShutdownPrivilege
=
175 1, PRIVILEGE_SET_ALL_NECESSARY
,
176 { {{SE_SHUTDOWN_PRIVILEGE
, 0}, 0} }
181 TRACE("UserInitiateShutdown\n");
183 /* Get the caller's LUID */
184 Status
= GetProcessLuid(Thread
, NULL
, &CallerLuid
);
185 if (!NT_SUCCESS(Status
))
187 ERR("UserInitiateShutdown: GetProcessLuid failed\n");
192 * Check if this is the System LUID, and adjust flags if needed.
193 * In particular, be sure there is no EWX_CALLER_SYSTEM flag
194 * spuriously set (could be the sign of malicous app!).
196 if (RtlEqualLuid(&CallerLuid
, &SystemLuid
))
197 Flags
|= EWX_CALLER_SYSTEM
;
199 Flags
&= ~EWX_CALLER_SYSTEM
;
203 /* Retrieve the Win32 process info */
204 ppi
= PsGetProcessWin32Process(PsGetThreadProcess(Thread
));
207 ERR("UserInitiateShutdown: Failed to get win32 thread!\n");
208 return STATUS_INVALID_HANDLE
;
211 /* If the caller is not Winlogon, do some security checks */
212 if (PsGetThreadProcessId(Thread
) != gpidLogon
)
215 * Here also, be sure there is no EWX_CALLER_WINLOGON flag
216 * spuriously set (could be the sign of malicous app!).
218 Flags
&= ~EWX_CALLER_WINLOGON
;
222 /* Check whether the current process is attached to a window station */
223 if (ppi
->prpwinsta
== NULL
)
225 ERR("UserInitiateShutdown: Process is not attached to a desktop\n");
226 return STATUS_INVALID_HANDLE
;
229 /* Check whether the window station of the current process can send exit requests */
230 if (!RtlAreAllAccessesGranted(ppi
->amwinsta
, WINSTA_EXITWINDOWS
))
232 ERR("UserInitiateShutdown: Caller doesn't have the rights to shutdown\n");
233 return STATUS_ACCESS_DENIED
;
237 * NOTE: USERSRV automatically adds the shutdown flag when we poweroff or reboot.
239 * If the caller wants to shutdown / reboot / power-off...
241 if (Flags
& EWX_SHUTDOWN
)
243 /* ... check whether it has shutdown privilege */
244 if (!HasPrivilege(&ShutdownPrivilege
))
246 ERR("UserInitiateShutdown: Caller doesn't have the rights to shutdown\n");
247 return STATUS_PRIVILEGE_NOT_HELD
;
253 * ... but if it just wants to log-off, in case its
254 * window station is a non-IO one, fail the call.
256 if (ppi
->prpwinsta
->Flags
& WSS_NOIO
)
258 ERR("UserInitiateShutdown: Caller doesn't have the rights to logoff\n");
259 return STATUS_INVALID_DEVICE_REQUEST
;
264 /* If the caller is not Winlogon, possibly notify it to perform the real shutdown */
265 if (PsGetThreadProcessId(Thread
) != gpidLogon
)
267 // FIXME: HACK!! Do more checks!!
268 TRACE("UserInitiateShutdown: Notify Winlogon for shutdown\n");
269 NotifyLogon(hwndSAS
, &CallerLuid
, Flags
, STATUS_SUCCESS
);
270 return STATUS_PENDING
;
273 // If we reach this point, that means it's Winlogon that triggered the shutdown.
274 TRACE("UserInitiateShutdown: Winlogon is doing a shutdown\n");
277 * Update and save the shutdown flags globally for renotifying
278 * Winlogon if needed, when calling EndShutdown.
280 Flags
|= EWX_CALLER_WINLOGON
; // Winlogon is doing a shutdown, be sure the internal flag is set.
283 /* Save the shutdown flags now */
284 gdwShutdownFlags
= Flags
;
286 return STATUS_SUCCESS
;
290 UserEndShutdown(IN PETHREAD Thread
,
291 IN NTSTATUS ShutdownStatus
)
297 TRACE("UserEndShutdown called\n");
300 * FIXME: Some cleanup should be done when shutdown succeeds,
301 * and some reset should be done when shutdown is cancelled.
305 Status
= GetProcessLuid(Thread
, NULL
, &CallerLuid
);
306 if (!NT_SUCCESS(Status
))
308 ERR("UserEndShutdown: GetProcessLuid failed\n");
312 /* Copy the global flags because we're going to modify them for our purposes */
313 Flags
= gdwShutdownFlags
;
315 if (NT_SUCCESS(ShutdownStatus
))
317 /* Just report success, and keep the shutdown flags as they are */
318 ShutdownStatus
= STATUS_SUCCESS
;
322 /* Report the status to Winlogon and say that we cancel the shutdown */
323 Flags
|= EWX_SHUTDOWN_CANCELED
;
324 // FIXME: Should we reset gdwShutdownFlags to 0 ??
327 TRACE("UserEndShutdown: Notify Winlogon for end of shutdown\n");
328 NotifyLogon(hwndSAS
, &CallerLuid
, Flags
, ShutdownStatus
);
330 /* Always return success */
331 return STATUS_SUCCESS
;