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 ERR("\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 ERR("UserInitiateShutdown\n");
219 /* Get the caller's LUID */
220 Status
= GetProcessLuid(Thread
, &CallerLuid
);
221 if (!NT_SUCCESS(Status
))
223 ERR("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
));
242 return STATUS_INVALID_HANDLE
;
244 /* If the caller is not Winlogon, do some security checks */
245 if (PsGetThreadProcessId(Thread
) != gpidLogon
)
248 * Here also, be sure there is no EWX_CALLER_WINLOGON flag
249 * spuriously set (could be the sign of malicous app!).
251 Flags
&= ~EWX_CALLER_WINLOGON
;
255 /* Check whether the current process is attached to a window station */
256 if (ppi
->prpwinsta
== NULL
)
257 return STATUS_INVALID_HANDLE
;
259 /* Check whether the window station of the current process can send exit requests */
260 if (!RtlAreAllAccessesGranted(ppi
->amwinsta
, WINSTA_EXITWINDOWS
))
261 return STATUS_ACCESS_DENIED
;
264 * NOTE: USERSRV automatically adds the shutdown flag when we poweroff or reboot.
266 * If the caller wants to shutdown / reboot / power-off...
268 if (Flags
& EWX_SHUTDOWN
)
270 /* ... check whether it has shutdown privilege */
271 if (!HasPrivilege(&ShutdownPrivilege
))
272 return STATUS_PRIVILEGE_NOT_HELD
;
277 * ... but if it just wants to log-off, in case its
278 * window station is a non-IO one, fail the call.
280 if (ppi
->prpwinsta
->Flags
& WSS_NOIO
)
281 return STATUS_INVALID_DEVICE_REQUEST
;
285 /* If the caller is not Winlogon, possibly notify it to perform the real shutdown */
286 if (PsGetThreadProcessId(Thread
) != gpidLogon
)
288 // FIXME: HACK!! Do more checks!!
289 ERR("UserInitiateShutdown -- Notify Winlogon for shutdown\n");
290 NotifyLogon(hwndSAS
, &CallerLuid
, Flags
, STATUS_SUCCESS
);
291 return STATUS_PENDING
;
294 // If we reach this point, that means it's Winlogon that triggered the shutdown.
295 ERR("UserInitiateShutdown -- Winlogon is doing a shutdown\n");
298 * Update and save the shutdown flags globally for renotifying
299 * Winlogon if needed, when calling EndShutdown.
301 Flags
|= EWX_CALLER_WINLOGON
; // Winlogon is doing a shutdown, be sure the internal flag is set.
304 /* Save the shutdown flags now */
305 gdwShutdownFlags
= Flags
;
307 return STATUS_SUCCESS
;
311 UserEndShutdown(IN PETHREAD Thread
,
312 IN NTSTATUS ShutdownStatus
)
318 ERR("UserEndShutdown\n");
321 * FIXME: Some cleanup should be done when shutdown succeeds,
322 * and some reset should be done when shutdown is cancelled.
326 Status
= GetProcessLuid(Thread
, &CallerLuid
);
327 if (!NT_SUCCESS(Status
))
329 ERR("GetProcessLuid failed\n");
333 /* Copy the global flags because we're going to modify them for our purposes */
334 Flags
= gdwShutdownFlags
;
336 if (NT_SUCCESS(ShutdownStatus
))
338 /* Just report success, and keep the shutdown flags as they are */
339 ShutdownStatus
= STATUS_SUCCESS
;
343 /* Report the status to Winlogon and say that we cancel the shutdown */
344 Flags
|= EWX_SHUTDOWN_CANCELED
;
345 // FIXME: Should we reset gdwShutdownFlags to 0 ??
348 ERR("UserEndShutdown -- Notify Winlogon for end of shutdown\n");
349 NotifyLogon(hwndSAS
, &CallerLuid
, Flags
, ShutdownStatus
);
351 /* Always return success */
352 return STATUS_SUCCESS
;