|
| 1 | +/* |
| 2 | + * PROJECT: ReactOS Win32K |
| 3 | + * LICENSE: GPL - See COPYING in the top level directory |
| 4 | + * FILE: server/main/misc.c |
| 5 | + * PURPOSE: Shutdown code |
| 6 | + * PROGRAMMERS: Aleksey Bragin ([email protected]) |
| 7 | + */ |
| 8 | + |
| 9 | +/* INCLUDES ******************************************************************/ |
| 10 | + |
| 11 | +#include <win32k.h> |
| 12 | + |
| 13 | +//#define NDEBUG |
| 14 | +#include <debug.h> |
| 15 | + |
| 16 | +#include <ntstatus.h> |
| 17 | + |
| 18 | +/* Our local copy of shutdown flags */ |
| 19 | +static ULONG gdwShutdownFlags = 0; |
| 20 | + |
| 21 | +HWND hwndSAS = NULL; |
| 22 | + |
| 23 | +NTSTATUS |
| 24 | +GetProcessLuid(IN PETHREAD Thread OPTIONAL, |
| 25 | + OUT PLUID Luid) |
| 26 | +{ |
| 27 | + NTSTATUS Status; |
| 28 | + PACCESS_TOKEN Token; |
| 29 | + SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; |
| 30 | + BOOLEAN CopyOnOpen, EffectiveOnly; |
| 31 | + |
| 32 | + if (Thread == NULL) |
| 33 | + Thread = PsGetCurrentThread(); |
| 34 | + |
| 35 | + /* Use a thread token */ |
| 36 | + Token = PsReferenceImpersonationToken(Thread, |
| 37 | + &CopyOnOpen, |
| 38 | + &EffectiveOnly, |
| 39 | + &ImpersonationLevel); |
| 40 | + if (Token == NULL) |
| 41 | + { |
| 42 | + /* We don't have a thread token, use a process token */ |
| 43 | + Token = PsReferencePrimaryToken(PsGetThreadProcess(Thread)); |
| 44 | + |
| 45 | + /* If no token, fail */ |
| 46 | + if (Token == NULL) |
| 47 | + return STATUS_NO_TOKEN; |
| 48 | + } |
| 49 | + |
| 50 | + /* Query the LUID */ |
| 51 | + Status = SeQueryAuthenticationIdToken(Token, Luid); |
| 52 | + |
| 53 | + /* Get rid of the token and return */ |
| 54 | + ObDereferenceObject(Token); |
| 55 | + return Status; |
| 56 | +} |
| 57 | + |
| 58 | +BOOL |
| 59 | +NotifyLogon(IN HWND hWndSta, |
| 60 | + IN PLUID CallerLuid, |
| 61 | + IN ULONG Flags, |
| 62 | + IN NTSTATUS ShutdownStatus) |
| 63 | +{ |
| 64 | + // LUID SystemLuid = SYSTEM_LUID; |
| 65 | + ULONG Notif, Param; |
| 66 | + |
| 67 | + DPRINT("NotifyLogon(0x%lx, 0x%lx)\n", Flags, ShutdownStatus); |
| 68 | + |
| 69 | + /* If no Winlogon notifications are needed, just return */ |
| 70 | + if (Flags & EWX_NONOTIFY) |
| 71 | + return FALSE; |
| 72 | + |
| 73 | + /* In case we cancelled the shutdown...*/ |
| 74 | + if (Flags & EWX_SHUTDOWN_CANCELED) |
| 75 | + { |
| 76 | + /* ... send a LN_LOGOFF_CANCELED to Winlogon with the real cancel status... */ |
| 77 | + Notif = LN_LOGOFF_CANCELED; |
| 78 | + Param = ShutdownStatus; |
| 79 | + } |
| 80 | + else |
| 81 | + { |
| 82 | + /* ... otherwise it's a real logoff. Send the shutdown flags in that case. */ |
| 83 | + Notif = LN_LOGOFF; |
| 84 | + Param = Flags; |
| 85 | + } |
| 86 | + |
| 87 | + // FIXME: At the moment, always send the notifications... In real world some checks are done. |
| 88 | + // if (hwndSAS && ( (Flags & EWX_SHUTDOWN) || RtlEqualLuid(CallerLuid, &SystemLuid)) ) |
| 89 | + if (hwndSAS) |
| 90 | + { |
| 91 | + DPRINT("\tSending %s, Param 0x%x message to Winlogon\n", Notif == LN_LOGOFF ? "LN_LOGOFF" : "LN_LOGOFF_CANCELED", Param); |
| 92 | + //UserPostMessage(hwndSAS, WM_LOGONNOTIFY, Notif, (LPARAM)Param); |
| 93 | + UNIMPLEMENTED; |
| 94 | + return TRUE; |
| 95 | + } |
| 96 | + else |
| 97 | + { |
| 98 | + DPRINT1("hwndSAS == NULL\n"); |
| 99 | + } |
| 100 | + |
| 101 | + return FALSE; |
| 102 | +} |
| 103 | + |
| 104 | + |
| 105 | +NTSTATUS |
| 106 | +UserInitiateShutdown(IN PETHREAD Thread, |
| 107 | + IN OUT PULONG pFlags) |
| 108 | +{ |
| 109 | + NTSTATUS Status; |
| 110 | + ULONG Flags = *pFlags; |
| 111 | + LUID CallerLuid; |
| 112 | + LUID SystemLuid = SYSTEM_LUID; |
| 113 | + /*static PRIVILEGE_SET ShutdownPrivilege = |
| 114 | + { |
| 115 | + 1, PRIVILEGE_SET_ALL_NECESSARY, |
| 116 | + { {{SE_SHUTDOWN_PRIVILEGE, 0}, 0} } |
| 117 | + };*/ |
| 118 | + |
| 119 | + PPROCESSINFO ppi; |
| 120 | + |
| 121 | + DPRINT("UserInitiateShutdown\n"); |
| 122 | + |
| 123 | + /* Get the caller's LUID */ |
| 124 | + Status = GetProcessLuid(Thread, &CallerLuid); |
| 125 | + if (!NT_SUCCESS(Status)) |
| 126 | + { |
| 127 | + DPRINT1("GetProcessLuid failed\n"); |
| 128 | + return Status; |
| 129 | + } |
| 130 | + |
| 131 | + /* |
| 132 | + * Check if this is the System LUID, and adjust flags if needed. |
| 133 | + * In particular, be sure there is no EWX_CALLER_SYSTEM flag |
| 134 | + * spuriously set (could be the sign of malicous app!). |
| 135 | + */ |
| 136 | + if (RtlEqualLuid(&CallerLuid, &SystemLuid)) |
| 137 | + Flags |= EWX_CALLER_SYSTEM; |
| 138 | + else |
| 139 | + Flags &= ~EWX_CALLER_SYSTEM; |
| 140 | + |
| 141 | + *pFlags = Flags; |
| 142 | + |
| 143 | + /* Retrieve the Win32 process info */ |
| 144 | + ppi = PsGetProcessWin32Process(PsGetThreadProcess(Thread)); |
| 145 | + if (ppi == NULL) |
| 146 | + return STATUS_INVALID_HANDLE; |
| 147 | + |
| 148 | +#if 0 |
| 149 | + /* If the caller is not Winlogon, do some security checks */ |
| 150 | + if (PsGetThreadProcessId(Thread) != gpidLogon) |
| 151 | + { |
| 152 | + /* |
| 153 | + * Here also, be sure there is no EWX_CALLER_WINLOGON flag |
| 154 | + * spuriously set (could be the sign of malicous app!). |
| 155 | + */ |
| 156 | + Flags &= ~EWX_CALLER_WINLOGON; |
| 157 | + |
| 158 | + *pFlags = Flags; |
| 159 | + |
| 160 | + /* Check whether the current process is attached to a window station */ |
| 161 | + if (ppi->prpwinsta == NULL) |
| 162 | + return STATUS_INVALID_HANDLE; |
| 163 | + |
| 164 | + /* Check whether the window station of the current process can send exit requests */ |
| 165 | + if (!RtlAreAllAccessesGranted(ppi->amwinsta, WINSTA_EXITWINDOWS)) |
| 166 | + return STATUS_ACCESS_DENIED; |
| 167 | + |
| 168 | + /* |
| 169 | + * NOTE: USERSRV automatically adds the shutdown flag when we poweroff or reboot. |
| 170 | + * |
| 171 | + * If the caller wants to shutdown / reboot / power-off... |
| 172 | + */ |
| 173 | + if (Flags & EWX_SHUTDOWN) |
| 174 | + { |
| 175 | + /* ... check whether it has shutdown privilege */ |
| 176 | + if (!HasPrivilege(&ShutdownPrivilege)) |
| 177 | + return STATUS_PRIVILEGE_NOT_HELD; |
| 178 | + } |
| 179 | + else |
| 180 | + { |
| 181 | + /* |
| 182 | + * ... but if it just wants to log-off, in case its |
| 183 | + * window station is a non-IO one, fail the call. |
| 184 | + */ |
| 185 | + if (ppi->prpwinsta->Flags & WSS_NOIO) |
| 186 | + return STATUS_INVALID_DEVICE_REQUEST; |
| 187 | + } |
| 188 | + } |
| 189 | + |
| 190 | + /* If the caller is not Winlogon, possibly notify it to perform the real shutdown */ |
| 191 | + if (PsGetThreadProcessId(Thread) != gpidLogon) |
| 192 | + { |
| 193 | + // FIXME: HACK!! Do more checks!! |
| 194 | + ERR("UserInitiateShutdown -- Notify Winlogon for shutdown\n"); |
| 195 | + NotifyLogon(hwndSAS, &CallerLuid, Flags, STATUS_SUCCESS); |
| 196 | + return STATUS_PENDING; |
| 197 | + } |
| 198 | + |
| 199 | + // If we reach this point, that means it's Winlogon that triggered the shutdown. |
| 200 | + ERR("UserInitiateShutdown -- Winlogon is doing a shutdown\n"); |
| 201 | + |
| 202 | + /* |
| 203 | + * Update and save the shutdown flags globally for renotifying |
| 204 | + * Winlogon if needed, when calling EndShutdown. |
| 205 | + */ |
| 206 | + Flags |= EWX_CALLER_WINLOGON; // Winlogon is doing a shutdown, be sure the internal flag is set. |
| 207 | + *pFlags = Flags; |
| 208 | + |
| 209 | + /* Save the shutdown flags now */ |
| 210 | + gdwShutdownFlags = Flags; |
| 211 | +#else |
| 212 | + UNIMPLEMENTED; |
| 213 | +#endif |
| 214 | + return STATUS_SUCCESS; |
| 215 | +} |
| 216 | + |
| 217 | +NTSTATUS |
| 218 | +UserEndShutdown(IN PETHREAD Thread, |
| 219 | + IN NTSTATUS ShutdownStatus) |
| 220 | +{ |
| 221 | + NTSTATUS Status; |
| 222 | + ULONG Flags; |
| 223 | + LUID CallerLuid; |
| 224 | + |
| 225 | + DPRINT("UserEndShutdown\n"); |
| 226 | + |
| 227 | + /* |
| 228 | + * FIXME: Some cleanup should be done when shutdown succeeds, |
| 229 | + * and some reset should be done when shutdown is cancelled. |
| 230 | + */ |
| 231 | + Status = GetProcessLuid(Thread, &CallerLuid); |
| 232 | + if (!NT_SUCCESS(Status)) |
| 233 | + { |
| 234 | + DPRINT1("GetProcessLuid failed\n"); |
| 235 | + return Status; |
| 236 | + } |
| 237 | + |
| 238 | + /* Copy the global flags because we're going to modify them for our purposes */ |
| 239 | + Flags = gdwShutdownFlags; |
| 240 | + |
| 241 | + if (NT_SUCCESS(ShutdownStatus)) |
| 242 | + { |
| 243 | + /* Just report success, and keep the shutdown flags as they are */ |
| 244 | + ShutdownStatus = STATUS_SUCCESS; |
| 245 | + } |
| 246 | + else |
| 247 | + { |
| 248 | + /* Report the status to Winlogon and say that we cancel the shutdown */ |
| 249 | + Flags |= EWX_SHUTDOWN_CANCELED; |
| 250 | + // FIXME: Should we reset gdwShutdownFlags to 0 ?? |
| 251 | + } |
| 252 | + |
| 253 | + DPRINT("UserEndShutdown -- Notify Winlogon for end of shutdown\n"); |
| 254 | + |
| 255 | +#if 0 |
| 256 | + NotifyLogon(hwndSAS, &CallerLuid, Flags, ShutdownStatus); |
| 257 | +#else |
| 258 | + UNIMPLEMENTED |
| 259 | +#endif |
| 260 | + |
| 261 | + /* Always return success */ |
| 262 | + return STATUS_SUCCESS; |
| 263 | +} |
| 264 | + |
| 265 | +/* EOF */ |
0 commit comments