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
;
93 GetProcessLuid(IN PETHREAD Thread OPTIONAL
,
98 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
;
99 BOOLEAN CopyOnOpen
, EffectiveOnly
;
102 Thread
= PsGetCurrentThread();
104 /* Use a thread token */
105 Token
= PsReferenceImpersonationToken(Thread
,
108 &ImpersonationLevel
);
111 /* We don't have a thread token, use a process token */
112 Token
= PsReferencePrimaryToken(PsGetThreadProcess(Thread
));
114 /* If no token, fail */
116 return STATUS_NO_TOKEN
;
120 Status
= SeQueryAuthenticationIdToken(Token
, Luid
);
122 /* Get rid of the token and return */
123 ObDereferenceObject(Token
);
128 HasPrivilege(IN PPRIVILEGE_SET Privilege
)
131 SECURITY_SUBJECT_CONTEXT SubjectContext
;
133 /* Capture and lock the security subject context */
134 SeCaptureSubjectContext(&SubjectContext
);
135 SeLockSubjectContext(&SubjectContext
);
137 /* Do privilege check */
138 Result
= SePrivilegeCheck(Privilege
, &SubjectContext
, UserMode
);
140 /* Audit the privilege */
142 SePrivilegeObjectAuditAlarm(NULL
,
150 /* Unlock and release the security subject context and return */
151 SeUnlockSubjectContext(&SubjectContext
);
152 SeReleaseSubjectContext(&SubjectContext
);
157 NotifyLogon(IN HWND hWndSta
,
160 IN NTSTATUS ShutdownStatus
)
162 // LUID SystemLuid = SYSTEM_LUID;
165 ERR("NotifyLogon(0x%lx, 0x%lx)\n", Flags
, ShutdownStatus
);
167 /* If no Winlogon notifications are needed, just return */
168 if (Flags
& EWX_NONOTIFY
)
171 /* In case we cancelled the shutdown...*/
172 if (Flags
& EWX_SHUTDOWN_CANCELED
)
174 /* ... send a LN_LOGOFF_CANCELED to Winlogon with the real cancel status... */
175 Notif
= LN_LOGOFF_CANCELED
;
176 Param
= ShutdownStatus
;
180 /* ... otherwise it's a real logoff. Send the shutdown flags in that case. */
185 // FIXME: At the moment, always send the notifications... In real world some checks are done.
186 // if (hwndSAS && ( (Flags & EWX_SHUTDOWN) || RtlEqualLuid(CallerLuid, &SystemLuid)) )
189 TRACE("\tSending %s message to Winlogon\n", Notif
== LN_LOGOFF
? "LN_LOGOFF" : "LN_LOGOFF_CANCELED");
190 UserPostMessage(hwndSAS
, WM_LOGONNOTIFY
, Notif
, (LPARAM
)Param
);
195 ERR("hwndSAS == NULL\n");
202 UserInitiateShutdown(IN PETHREAD Thread
,
203 IN OUT PULONG pFlags
)
206 ULONG Flags
= *pFlags
;
208 LUID SystemLuid
= SYSTEM_LUID
;
209 static PRIVILEGE_SET ShutdownPrivilege
=
211 1, PRIVILEGE_SET_ALL_NECESSARY
,
212 { {{SE_SHUTDOWN_PRIVILEGE
, 0}, 0} }
217 TRACE("UserInitiateShutdown\n");
219 /* Get the caller's LUID */
220 Status
= GetProcessLuid(Thread
, &CallerLuid
);
221 if (!NT_SUCCESS(Status
))
223 ERR("UserInitiateShutdown: GetProcessLuid failed\n");
228 * Check if this is the System LUID, and adjust flags if needed.
229 * In particular, be sure there is no EWX_CALLER_SYSTEM flag
230 * spuriously set (could be the sign of malicous app!).
232 if (RtlEqualLuid(&CallerLuid
, &SystemLuid
))
233 Flags
|= EWX_CALLER_SYSTEM
;
235 Flags
&= ~EWX_CALLER_SYSTEM
;
239 /* Retrieve the Win32 process info */
240 ppi
= PsGetProcessWin32Process(PsGetThreadProcess(Thread
));
243 ERR("UserInitiateShutdown: Failed to get win32 thread!\n");
244 return STATUS_INVALID_HANDLE
;
247 /* If the caller is not Winlogon, do some security checks */
248 if (PsGetThreadProcessId(Thread
) != gpidLogon
)
251 * Here also, be sure there is no EWX_CALLER_WINLOGON flag
252 * spuriously set (could be the sign of malicous app!).
254 Flags
&= ~EWX_CALLER_WINLOGON
;
258 /* Check whether the current process is attached to a window station */
259 if (ppi
->prpwinsta
== NULL
)
261 ERR("UserInitiateShutdown: Process is not attached to a desktop\n");
262 return STATUS_INVALID_HANDLE
;
265 /* Check whether the window station of the current process can send exit requests */
266 if (!RtlAreAllAccessesGranted(ppi
->amwinsta
, WINSTA_EXITWINDOWS
))
268 ERR("UserInitiateShutdown: Caller doesn't have the rights to shutdown\n");
269 return STATUS_ACCESS_DENIED
;
273 * NOTE: USERSRV automatically adds the shutdown flag when we poweroff or reboot.
275 * If the caller wants to shutdown / reboot / power-off...
277 if (Flags
& EWX_SHUTDOWN
)
279 /* ... check whether it has shutdown privilege */
280 if (!HasPrivilege(&ShutdownPrivilege
))
282 ERR("UserInitiateShutdown: Caller doesn't have the rights to shutdown\n");
283 return STATUS_PRIVILEGE_NOT_HELD
;
289 * ... but if it just wants to log-off, in case its
290 * window station is a non-IO one, fail the call.
292 if (ppi
->prpwinsta
->Flags
& WSS_NOIO
)
294 ERR("UserInitiateShutdown: Caller doesn't have the rights to logoff\n");
295 return STATUS_INVALID_DEVICE_REQUEST
;
300 /* If the caller is not Winlogon, possibly notify it to perform the real shutdown */
301 if (PsGetThreadProcessId(Thread
) != gpidLogon
)
303 // FIXME: HACK!! Do more checks!!
304 TRACE("UserInitiateShutdown: Notify Winlogon for shutdown\n");
305 NotifyLogon(hwndSAS
, &CallerLuid
, Flags
, STATUS_SUCCESS
);
306 return STATUS_PENDING
;
309 // If we reach this point, that means it's Winlogon that triggered the shutdown.
310 TRACE("UserInitiateShutdown: Winlogon is doing a shutdown\n");
313 * Update and save the shutdown flags globally for renotifying
314 * Winlogon if needed, when calling EndShutdown.
316 Flags
|= EWX_CALLER_WINLOGON
; // Winlogon is doing a shutdown, be sure the internal flag is set.
319 /* Save the shutdown flags now */
320 gdwShutdownFlags
= Flags
;
322 return STATUS_SUCCESS
;
326 UserEndShutdown(IN PETHREAD Thread
,
327 IN NTSTATUS ShutdownStatus
)
333 TRACE("UserEndShutdown called\n");
336 * FIXME: Some cleanup should be done when shutdown succeeds,
337 * and some reset should be done when shutdown is cancelled.
341 Status
= GetProcessLuid(Thread
, &CallerLuid
);
342 if (!NT_SUCCESS(Status
))
344 ERR("GetProcessLuid failed\n");
348 /* Copy the global flags because we're going to modify them for our purposes */
349 Flags
= gdwShutdownFlags
;
351 if (NT_SUCCESS(ShutdownStatus
))
353 /* Just report success, and keep the shutdown flags as they are */
354 ShutdownStatus
= STATUS_SUCCESS
;
358 /* Report the status to Winlogon and say that we cancel the shutdown */
359 Flags
|= EWX_SHUTDOWN_CANCELED
;
360 // FIXME: Should we reset gdwShutdownFlags to 0 ??
363 TRACE("UserEndShutdown: Notify Winlogon for end of shutdown\n");
364 NotifyLogon(hwndSAS
, &CallerLuid
, Flags
, ShutdownStatus
);
366 /* Always return success */
367 return STATUS_SUCCESS
;