Author: hbelusca
Date: Sat Feb  7 15:26:42 2015
New Revision: 66192
URL: 
http://svn.reactos.org/svn/reactos?rev=66192&view=rev
Log:
[WINLOGON][WIN32K]
Move the shutdown privilege check from winlogon to win32k (function
"UserInitiateShutdown") as it should be done.
[WIN32K]
- Introduce the pair of UserInitiateShutdown/UserEndShutdown calls that should be called
when WINSRV starts a shutdown (and when it finishes it). In particular it is in
UserInitiateShutdown that we need to check whether the caller has the rights to perform a
shutdown (it should also have a valid window station).
- Remove the ROS-specific TWOPARAM_ROUTINE_EXITREACTOS call that is traded for
Win2k3-compatible call to UserInitiateShutdown.
[WINSRV]
Hackfix our current ExitWindowsEx functionality (based on a patch by Alex made against
r46050 for win32csr) to make it "compatible" with the improvements in win32k:
impersonate the caller and call the UserInitiateShutdown win32k system call (instead of
the TWOPARAM_ROUTINE_EXITREACTOS). More will come later on.
[USER32]
Win32k can require performing shutdown in an asynchronous way (needed also on WINSRV side)
so we need to put all the code in a worker thread.
Part 8/X (part 6 was r65693 and part 7 was r66186).
CORE-8322 #comment Start to add Alex' win32csr shutdown patch in WINSRV; fixes for
winlogon and additions to win32k.
Modified:
    trunk/reactos/base/system/winlogon/sas.c
    trunk/reactos/win32ss/include/ntuser.h
    trunk/reactos/win32ss/user/ntuser/ntstubs.c
    trunk/reactos/win32ss/user/ntuser/shutdown.c
    trunk/reactos/win32ss/user/ntuser/shutdown.h
    trunk/reactos/win32ss/user/ntuser/simplecall.c
    trunk/reactos/win32ss/user/user32/misc/exit.c
    trunk/reactos/win32ss/user/winsrv/usersrv/shutdown.c
Modified: trunk/reactos/base/system/winlogon/sas.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/system/winlogon/sas.c…
==============================================================================
--- trunk/reactos/base/system/winlogon/sas.c    [iso-8859-1] (original)
+++ trunk/reactos/base/system/winlogon/sas.c    [iso-8859-1] Sat Feb  7 15:26:42 2015
@@ -1147,67 +1147,6 @@
     return TRUE;
 }
-
-#if 0
-static
-NTSTATUS
-CheckForShutdownPrivilege(
-    IN DWORD RequestingProcessId)
-{
-    HANDLE Process;
-    HANDLE Token;
-    BOOL CheckResult;
-    PPRIVILEGE_SET PrivSet;
-
-    TRACE("CheckForShutdownPrivilege()\n");
-
-    Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, RequestingProcessId);
-    if (!Process)
-    {
-        WARN("OpenProcess() failed with error %lu\n", GetLastError());
-        return STATUS_INVALID_HANDLE;
-    }
-    if (!OpenProcessToken(Process, TOKEN_QUERY, &Token))
-    {
-        WARN("OpenProcessToken() failed with error %lu\n", GetLastError());
-        CloseHandle(Process);
-        return STATUS_INVALID_HANDLE;
-    }
-    CloseHandle(Process);
-    PrivSet = HeapAlloc(GetProcessHeap(), 0, sizeof(PRIVILEGE_SET) +
sizeof(LUID_AND_ATTRIBUTES));
-    if (!PrivSet)
-    {
-        ERR("Failed to allocate mem for privilege set\n");
-        CloseHandle(Token);
-        return STATUS_NO_MEMORY;
-    }
-    PrivSet->PrivilegeCount = 1;
-    PrivSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
-    if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
&PrivSet->Privilege[0].Luid))
-    {
-        WARN("LookupPrivilegeValue() failed with error %lu\n", GetLastError());
-        HeapFree(GetProcessHeap(), 0, PrivSet);
-        CloseHandle(Token);
-        return STATUS_UNSUCCESSFUL;
-    }
-    if (!PrivilegeCheck(Token, PrivSet, &CheckResult))
-    {
-        WARN("PrivilegeCheck() failed with error %lu\n", GetLastError());
-        HeapFree(GetProcessHeap(), 0, PrivSet);
-        CloseHandle(Token);
-        return STATUS_ACCESS_DENIED;
-    }
-    HeapFree(GetProcessHeap(), 0, PrivSet);
-    CloseHandle(Token);
-
-    if (!CheckResult)
-    {
-        WARN("SE_SHUTDOWN privilege not enabled\n");
-        return STATUS_ACCESS_DENIED;
-    }
-    return STATUS_SUCCESS;
-}
-#endif
 BOOL
 WINAPI
@@ -1337,13 +1276,40 @@
                     UINT Action = Flags & EWX_ACTION_MASK;
                     DWORD wlxAction;
+                    TRACE("\tFlags : 0x%lx\n", lParam);
+
+                    /*
+                     * Our caller (USERSRV) should have added the shutdown flag
+                     * when setting also poweroff or reboot.
+                     */
+                    if (Action & (EWX_POWEROFF | EWX_REBOOT))
+                    {
+                        if ((Action & EWX_SHUTDOWN) == 0)
+                        {
+                            ERR("Missing EWX_SHUTDOWN flag for poweroff or reboot;
action 0x%x\n", Action);
+                            return STATUS_INVALID_PARAMETER;
+                        }
+
+                        /* Now we can locally remove it for performing checks */
+                        Action &= ~EWX_SHUTDOWN;
+                    }
+
                     /* Check parameters */
                     switch (Action)
                     {
-                        case EWX_LOGOFF: wlxAction = WLX_SAS_ACTION_LOGOFF; break;
-                        case EWX_SHUTDOWN: wlxAction = WLX_SAS_ACTION_SHUTDOWN; break;
-                        case EWX_REBOOT: wlxAction = WLX_SAS_ACTION_SHUTDOWN_REBOOT;
break;
-                        case EWX_POWEROFF: wlxAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
break;
+                        case EWX_LOGOFF:
+                            wlxAction = WLX_SAS_ACTION_LOGOFF;
+                            break;
+                        case EWX_SHUTDOWN:
+                            wlxAction = WLX_SAS_ACTION_SHUTDOWN;
+                            break;
+                        case EWX_REBOOT:
+                            wlxAction = WLX_SAS_ACTION_SHUTDOWN_REBOOT;
+                            break;
+                        case EWX_POWEROFF:
+                            wlxAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
+                            break;
+
                         default:
                         {
                             ERR("Invalid ExitWindows action 0x%x\n", Action);
@@ -1351,15 +1317,7 @@
                         }
                     }
-#if 0
-                    // FIXME: This check must be done by Win32k, not by us!
-                    if (WLX_SHUTTINGDOWN(wlxAction))
-                    {
-                        NTSTATUS Status = CheckForShutdownPrivilege(wParam);
-                        if (!NT_SUCCESS(Status))
-                            return Status;
-                    }
-#endif
+                    /* Now do the shutdown action proper */
                     DoGenericAction(Session, wlxAction);
                     return 1;
                 }
Modified: trunk/reactos/win32ss/include/ntuser.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/include/ntuser.h?r…
==============================================================================
--- trunk/reactos/win32ss/include/ntuser.h      [iso-8859-1] (original)
+++ trunk/reactos/win32ss/include/ntuser.h      [iso-8859-1] Sat Feb  7 15:26:42 2015
@@ -3439,9 +3439,8 @@
 #define ONEPARAM_ROUTINE_ENABLEPROCWNDGHSTING 0xfffe000d
 #define ONEPARAM_ROUTINE_GETDESKTOPMAPPING    0xfffe000e
 #define TWOPARAM_ROUTINE_SETMENUBARHEIGHT   0xfffd0050
-#define TWOPARAM_ROUTINE_EXITREACTOS        0xfffd0051
-#define TWOPARAM_ROUTINE_SETGUITHRDHANDLE   0xfffd0052
-#define HWNDLOCK_ROUTINE_SETFOREGROUNDWINDOWMOUSE 0xfffd0053
+#define TWOPARAM_ROUTINE_SETGUITHRDHANDLE   0xfffd0051
+#define HWNDLOCK_ROUTINE_SETFOREGROUNDWINDOWMOUSE 0xfffd0052
   #define MSQ_STATE_CAPTURE    0x1
   #define MSQ_STATE_ACTIVE     0x2
   #define MSQ_STATE_FOCUS      0x3
Modified: trunk/reactos/win32ss/user/ntuser/ntstubs.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/user/ntuser/ntstub…
==============================================================================
--- trunk/reactos/win32ss/user/ntuser/ntstubs.c [iso-8859-1] (original)
+++ trunk/reactos/win32ss/user/ntuser/ntstubs.c [iso-8859-1] Sat Feb  7 15:26:42 2015
@@ -412,7 +412,7 @@
 {
     NTSTATUS Status = STATUS_SUCCESS;
-    /* Allow only Console Server to perform this operation (via CSRSS) */
+    /* Allow only the Console Server to perform this operation (via CSRSS) */
     if (PsGetCurrentProcess() != gpepCSRSS)
         return STATUS_ACCESS_DENIED;
@@ -770,7 +770,6 @@
 {
     NTSTATUS Status = STATUS_SUCCESS;
     PETHREAD Thread;
-    HANDLE CsrPortHandle;
     /* Allow only CSRSS to perform this operation */
     if (PsGetCurrentProcess() != gpepCSRSS)
@@ -792,24 +791,54 @@
         case UserThreadInitiateShutdown:
         {
             ERR("Shutdown initiated\n");
-            STUB;
-            Status = STATUS_NOT_IMPLEMENTED;
+
+            if (ThreadInformationLength != sizeof(ULONG))
+            {
+                Status = STATUS_INFO_LENGTH_MISMATCH;
+                break;
+            }
+
+            Status = UserInitiateShutdown(Thread, (PULONG)ThreadInformation);
             break;
         }
         case UserThreadEndShutdown:
         {
+            NTSTATUS ShutdownStatus;
+
             ERR("Shutdown ended\n");
-            STUB;
-            Status = STATUS_NOT_IMPLEMENTED;
+
+            if (ThreadInformationLength != sizeof(ShutdownStatus))
+            {
+                Status = STATUS_INFO_LENGTH_MISMATCH;
+                break;
+            }
+
+            Status = STATUS_SUCCESS;
+            _SEH2_TRY
+            {
+                ProbeForRead(ThreadInformation, sizeof(ShutdownStatus), sizeof(PVOID));
+                ShutdownStatus = *(NTSTATUS*)ThreadInformation;
+            }
+            _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+            {
+                Status = _SEH2_GetExceptionCode();
+            }
+            _SEH2_END;
+
+            if (NT_SUCCESS(Status))
+                Status = UserEndShutdown(Thread, ShutdownStatus);
+
             break;
         }
         case UserThreadCsrApiPort:
         {
+            HANDLE CsrPortHandle;
+
             ERR("Set CSR API Port for Win32k\n");
-            if (ThreadInformationLength != sizeof(HANDLE))
+            if (ThreadInformationLength != sizeof(CsrPortHandle))
             {
                 Status = STATUS_INFO_LENGTH_MISMATCH;
                 break;
@@ -818,7 +847,7 @@
             Status = STATUS_SUCCESS;
             _SEH2_TRY
             {
-                ProbeForRead(ThreadInformation, sizeof(HANDLE), sizeof(PVOID));
+                ProbeForRead(ThreadInformation, sizeof(CsrPortHandle), sizeof(PVOID));
                 CsrPortHandle = *(PHANDLE)ThreadInformation;
             }
             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
@@ -828,9 +857,8 @@
             _SEH2_END;
             if (NT_SUCCESS(Status))
-            {
                 Status = InitCsrApiPort(CsrPortHandle);
-            }
+
             break;
         }
Modified: trunk/reactos/win32ss/user/ntuser/shutdown.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/user/ntuser/shutdo…
==============================================================================
--- trunk/reactos/win32ss/user/ntuser/shutdown.c        [iso-8859-1] (original)
+++ trunk/reactos/win32ss/user/ntuser/shutdown.c        [iso-8859-1] Sat Feb  7 15:26:42
2015
@@ -7,7 +7,7 @@
  */
 #include <win32k.h>
-// DBG_DEFAULT_CHANNEL(UserShutdown);
+DBG_DEFAULT_CHANNEL(UserShutdown);
 /*
  * Based on CSRSS and described in pages 1115 - 1118 "Windows Internals, Fifth
Edition".
@@ -85,4 +85,172 @@
     return lResult;
 }
+
+NTSTATUS
+GetProcessLuid(IN PETHREAD Thread OPTIONAL,
+               OUT PLUID Luid)
+{
+    NTSTATUS Status;
+    PACCESS_TOKEN Token;
+    SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
+    BOOLEAN CopyOnOpen, EffectiveOnly;
+
+    if (Thread == NULL)
+        Thread = PsGetCurrentThread();
+
+    /* Use a thread token */
+    Token = PsReferenceImpersonationToken(Thread,
+                                          &CopyOnOpen,
+                                          &EffectiveOnly,
+                                          &ImpersonationLevel);
+    if (Token == NULL)
+    {
+        /* We don't have a thread token, use a process token */
+        Token = PsReferencePrimaryToken(PsGetThreadProcess(Thread));
+
+        /* If no token, fail */
+        if (Token == NULL)
+            return STATUS_NO_TOKEN;
+    }
+
+    /* Query the LUID */
+    Status = SeQueryAuthenticationIdToken(Token, Luid);
+
+    /* Get rid of the token and return */
+    ObDereferenceObject(Token);
+    return Status;
+}
+
+BOOLEAN
+HasPrivilege(IN PPRIVILEGE_SET Privilege)
+{
+    BOOLEAN Result;
+    SECURITY_SUBJECT_CONTEXT SubjectContext;
+
+    /* Capture and lock the security subject context */
+    SeCaptureSubjectContext(&SubjectContext);
+    SeLockSubjectContext(&SubjectContext);
+
+    /* Do privilege check */
+    Result = SePrivilegeCheck(Privilege, &SubjectContext, UserMode);
+
+    /* Audit the privilege */
+#if 0
+    SePrivilegeObjectAuditAlarm(NULL,
+                                &SubjectContext,
+                                0,
+                                Privilege,
+                                Result,
+                                UserMode);
+#endif
+
+    /* Unlock and release the security subject context and return */
+    SeUnlockSubjectContext(&SubjectContext);
+    SeReleaseSubjectContext(&SubjectContext);
+    return Result;
+}
+
+NTSTATUS
+UserInitiateShutdown(IN PETHREAD Thread,
+                     IN OUT PULONG pFlags)
+{
+    NTSTATUS Status;
+    ULONG Flags = *pFlags;
+    LUID CallerLuid;
+    // LUID SystemLuid = SYSTEM_LUID;
+    static PRIVILEGE_SET ShutdownPrivilege =
+    {
+        1, PRIVILEGE_SET_ALL_NECESSARY,
+        { {{SE_SHUTDOWN_PRIVILEGE, 0}, 0} }
+    };
+
+    PPROCESSINFO ppi;
+
+    ERR("UserInitiateShutdown\n");
+
+    if(hwndSAS == NULL)
+        return STATUS_NOT_FOUND;
+
+    /* Get the caller's LUID */
+    Status = GetProcessLuid(Thread, &CallerLuid);
+    if (!NT_SUCCESS(Status))
+    {
+        ERR("GetProcessLuid failed\n");
+        return Status;
+    }
+
+    // FIXME: Check if this is the System LUID, and adjust flags if needed.
+    // if (RtlEqualLuid(&CallerLuid, &SystemLuid)) { Flags = ...; }
+    *pFlags = Flags;
+
+    /* Retrieve the Win32 process info */
+    ppi = PsGetProcessWin32Process(PsGetThreadProcess(Thread));
+    if (ppi == NULL)
+        return STATUS_INVALID_HANDLE;
+
+    /* If the caller is not Winlogon, do some security checks */
+    if (PsGetThreadProcessId(Thread) != gpidLogon)
+    {
+        // FIXME: Play again with flags...
+        *pFlags = Flags;
+
+        /* Check whether the current process is attached to a window station */
+        if (ppi->prpwinsta == NULL)
+            return STATUS_INVALID_HANDLE;
+
+        /* Check whether the window station of the current process can send exit requests
*/
+        if (!RtlAreAllAccessesGranted(ppi->amwinsta, WINSTA_EXITWINDOWS))
+            return STATUS_ACCESS_DENIED;
+
+        /*
+         * NOTE: USERSRV automatically adds the shutdown flag when we poweroff or reboot.
+         *
+         * If the caller wants to shutdown / reboot / power-off...
+         */
+        if (Flags & EWX_SHUTDOWN)
+        {
+            /* ... check whether it has shutdown privilege */
+            if (!HasPrivilege(&ShutdownPrivilege))
+                return STATUS_PRIVILEGE_NOT_HELD;
+        }
+        else
+        {
+            /*
+             * ... but if it just wants to log-off, in case its
+             * window station is a non-IO one, fail the call.
+             */
+            if (ppi->prpwinsta->Flags & WSS_NOIO)
+                return STATUS_INVALID_DEVICE_REQUEST;
+        }
+    }
+
+    /* If the caller is not Winlogon, notify it to perform the real shutdown */
+    if (PsGetThreadProcessId(Thread) != gpidLogon)
+    {
+        // FIXME: HACK!! Do more checks!!
+        UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_LOGOFF, (LPARAM)Flags);
+        return STATUS_PENDING;
+    }
+
+    // If we reach this point, that means it's Winlogon that triggered the shutdown.
+
+    /*
+     * FIXME:
+     * Update and save the shutdown flags globally for renotifying Winlogon
+     * if needed, when calling EndShutdown.
+     */
+    *pFlags = Flags;
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+UserEndShutdown(IN PETHREAD Thread,
+                IN NTSTATUS ShutdownStatus)
+{
+    ERR("UserEndShutdown\n");
+    STUB;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
 /* EOF */
Modified: trunk/reactos/win32ss/user/ntuser/shutdown.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/user/ntuser/shutdo…
==============================================================================
--- trunk/reactos/win32ss/user/ntuser/shutdown.h        [iso-8859-1] (original)
+++ trunk/reactos/win32ss/user/ntuser/shutdown.h        [iso-8859-1] Sat Feb  7 15:26:42
2015
@@ -4,3 +4,11 @@
 IntClientShutdown(IN PWND pWindow,
                   IN WPARAM wParam,
                   IN LPARAM lParam);
+
+NTSTATUS
+UserInitiateShutdown(IN PETHREAD Thread,
+                     IN OUT PULONG pFlags);
+
+NTSTATUS
+UserEndShutdown(IN PETHREAD Thread,
+                IN NTSTATUS ShutdownStatus);
Modified: trunk/reactos/win32ss/user/ntuser/simplecall.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/user/ntuser/simple…
==============================================================================
--- trunk/reactos/win32ss/user/ntuser/simplecall.c      [iso-8859-1] (original)
+++ trunk/reactos/win32ss/user/ntuser/simplecall.c      [iso-8859-1] Sat Feb  7 15:26:42
2015
@@ -7,8 +7,6 @@
  */
 #include <win32k.h>
-
-#include <winlogon.h>
 DBG_DEFAULT_CHANNEL(UserMisc);
@@ -449,18 +447,6 @@
       case TWOPARAM_ROUTINE_UNHOOKWINDOWSHOOK:
          RETURN( IntUnhookWindowsHook((int)Param1, (HOOKPROC)Param2));
-
-      case TWOPARAM_ROUTINE_EXITREACTOS:
-      {
-          UNREFERENCED_PARAMETER(Param1 /* ProcessId */);
-
-          if(hwndSAS == NULL)
-          {
-              ASSERT(hwndSAS);
-              RETURN(STATUS_NOT_FOUND);
-          }
-         RETURN(co_IntSendMessage(hwndSAS, WM_LOGONNOTIFY, LN_LOGOFF, Param2 /* Flags
*/));
-      }
    }
    ERR("Calling invalid routine number 0x%x in NtUserCallTwoParam(), Param1=0x%x
Parm2=0x%x\n",
            Routine, Param1, Param2);
Modified: trunk/reactos/win32ss/user/user32/misc/exit.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/user/user32/misc/e…
==============================================================================
--- trunk/reactos/win32ss/user/user32/misc/exit.c       [iso-8859-1] (original)
+++ trunk/reactos/win32ss/user/user32/misc/exit.c       [iso-8859-1] Sat Feb  7 15:26:42
2015
@@ -9,6 +9,8 @@
 #include <user32.h>
 #include <wine/debug.h>
+
+WINE_DEFAULT_DEBUG_CHANNEL(user32);
 /*
  * Sequence of events:
@@ -61,6 +63,130 @@
  *   the kernel and executive shutdown by calling NtShutdownSystem.
  */
+typedef struct
+{
+    UINT  uFlags;
+    DWORD dwReserved;
+} EXIT_REACTOS_DATA, *PEXIT_REACTOS_DATA;
+
+static BOOL
+ExitWindowsWorker(UINT uFlags,
+                  DWORD dwReserved,
+                  BOOL bCalledFromThread);
+
+static DWORD
+WINAPI
+ExitWindowsThread(LPVOID Param)
+{
+    DWORD dwExitCode;
+    PEXIT_REACTOS_DATA ExitData = (PEXIT_REACTOS_DATA)Param;
+
+    /* Do the exit asynchronously */
+    if (ExitWindowsWorker(ExitData->uFlags, ExitData->dwReserved, TRUE))
+        dwExitCode = ERROR_SUCCESS;
+    else
+        dwExitCode = GetLastError();
+
+    ExitThread(dwExitCode);
+    return ERROR_SUCCESS;
+}
+
+static BOOL
+ExitWindowsWorker(UINT uFlags,
+                  DWORD dwReserved,
+                  BOOL bCalledFromThread)
+{
+    EXIT_REACTOS_DATA ExitData;
+    HANDLE hExitThread;
+    DWORD ExitCode;
+    MSG msg;
+
+    USER_API_MESSAGE ApiMessage;
+    PUSER_EXIT_REACTOS ExitReactosRequest = &ApiMessage.Data.ExitReactosRequest;
+
+    /*
+     * 1- FIXME: Call NtUserCallOneParam(uFlags, ONEPARAM_ROUTINE_PREPAREFORLOGOFF);
+     *    If success we can continue, otherwise we must fail.
+     */
+
+    /*
+     * 2- Send the Exit request to CSRSS (and to Win32k indirectly).
+     *    We can shutdown synchronously or asynchronously.
+     */
+
+    // ExitReactosRequest->LastError = ERROR_SUCCESS;
+    ExitReactosRequest->Flags = uFlags;
+
+    CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+                        NULL,
+                        CSR_CREATE_API_NUMBER(USERSRV_SERVERDLL_INDEX,
UserpExitWindowsEx),
+                        sizeof(*ExitReactosRequest));
+
+    /* Set the last error accordingly */
+    if (NT_SUCCESS(ApiMessage.Status) || ApiMessage.Status == STATUS_CANT_WAIT)
+    {
+        if (ExitReactosRequest->LastError != ERROR_SUCCESS)
+            UserSetLastError(ExitReactosRequest->LastError);
+    }
+    else
+    {
+        UserSetLastNTError(ApiMessage.Status);
+        ExitReactosRequest->Success = FALSE;
+    }
+
+    /*
+     * In case CSR call succeeded and we did a synchronous exit
+     * (STATUS_CANT_WAIT is considered as a non-success status),
+     * return the real state of the operation now.
+     */
+    if (NT_SUCCESS(ApiMessage.Status))
+        return ExitReactosRequest->Success;
+
+    /*
+     * In case something failed: we have a non-success status and:
+     * - either we were doing a synchronous exit (Status != STATUS_CANT_WAIT), or
+     * - we were doing an asynchronous exit because we were called recursively via
+     *   another thread but we failed to exit,
+     * then bail out immediately, otherwise we would enter an infinite loop of exit
requests.
+     *
+     * On the contrary if we need to do an asynchronous exit (Status == STATUS_CANT_WAIT
+     * and not called recursively via another thread), then continue and do the exit.
+     */
+    if (ApiMessage.Status != STATUS_CANT_WAIT || bCalledFromThread)
+    {
+        UserSetLastNTError(ApiMessage.Status);
+        return FALSE;
+    }
+
+    /*
+     * 3- Win32k wants us to perform an asynchronous exit. Run the request in a thread.
+     * (ApiMessage.Status == STATUS_CANT_WAIT and not already called from a thread)
+     */
+    ExitData.uFlags     = uFlags;
+    ExitData.dwReserved = dwReserved;
+    hExitThread = CreateThread(NULL, 0, ExitWindowsThread, &ExitData, 0, NULL);
+    if (hExitThread == NULL)
+        return FALSE;
+
+    /* Pump and discard any input events sent to the app(s) */
+    while (MsgWaitForMultipleObjectsEx(1, &hExitThread, INFINITE, QS_ALLINPUT, 0))
+    {
+        while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+            DispatchMessageW(&msg);
+    }
+
+    /* Finally, return to caller */
+    if (!GetExitCodeThread(hExitThread, &ExitCode))
+        ExitCode = GetLastError();
+
+    CloseHandle(hExitThread);
+
+    if (ExitCode != ERROR_SUCCESS)
+        UserSetLastError(ExitCode);
+
+    return (ExitCode == ERROR_SUCCESS);
+}
+
 /*
  * @implemented
  */
@@ -68,23 +194,17 @@
 ExitWindowsEx(UINT uFlags,
               DWORD dwReserved)
 {
-    NTSTATUS Status;
-    USER_API_MESSAGE ApiMessage;
-
-    ApiMessage.Data.ExitReactosRequest.Flags = uFlags;
-    // ApiMessage.Data.ExitReactosRequest.Reserved = dwReserved;
-
-    Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
-                                 NULL,
-                                 CSR_CREATE_API_NUMBER(USERSRV_SERVERDLL_INDEX,
UserpExitWindowsEx),
-                                 sizeof(USER_EXIT_REACTOS));
-    if (!NT_SUCCESS(Status))
-    {
-        UserSetLastNTError(Status);
-        return FALSE;
-    }
-
-    return TRUE;
+    /*
+     * FIXME:
+     * 1- Calling the Exit worker must be done under certain conditions.
+     *    We may also need to warn the user if there are other people logged
+     *    on this computer (see
http://pve.proxmox.com/wiki/Windows_2003_guest_best_practices )
+     * 2- Call SrvRecordShutdownReason.
+     */
+
+    return ExitWindowsWorker(uFlags, dwReserved, FALSE);
+
+    /* FIXME: Call SrvRecordShutdownReason if we failed */
 }
 /*
Modified: trunk/reactos/win32ss/user/winsrv/usersrv/shutdown.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/win32ss/user/winsrv/usersr…
==============================================================================
--- trunk/reactos/win32ss/user/winsrv/usersrv/shutdown.c        [iso-8859-1] (original)
+++ trunk/reactos/win32ss/user/winsrv/usersrv/shutdown.c        [iso-8859-1] Sat Feb  7
15:26:42 2015
@@ -778,6 +778,7 @@
         return STATUS_ACCESS_DENIED;
     }
+    // FIXME: HAAAAACK!!
     DPRINT1("FIXME: Need to close all user processes!\n");
     return STATUS_SUCCESS;
@@ -896,12 +897,34 @@
 }
 static NTSTATUS FASTCALL
-UserExitReactos(DWORD UserProcessId, UINT Flags)
+UserExitReactos(PCSR_THREAD CsrThread, UINT Flags)
 {
     NTSTATUS Status;
-
-    /* FIXME Inside 2000 says we should impersonate the caller here */
-    Status = NtUserCallTwoParam(UserProcessId, Flags, TWOPARAM_ROUTINE_EXITREACTOS);
+    LUID CallerLuid;
+
+    // FIXME: HACK!!
+
+    /*
+     * Retrieve the caller's LUID so that we can only shutdown
+     * processes in the caller's LUID.
+     */
+    if (!CsrImpersonateClient(NULL))
+        return STATUS_BAD_IMPERSONATION_LEVEL;
+
+    Status = CsrGetProcessLuid(NULL, &CallerLuid);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Unable to get caller LUID, Status = 0x%08x\n", Status);
+        goto Quit;
+    }
+
+    DPRINT1("Caller LUID is: %lx.%lx\n", CallerLuid.HighPart,
CallerLuid.LowPart);
+
+    /* Notify Win32k and potentially Winlogon of the shutdown */
+    Status = NtUserSetInformationThread(CsrThread->ThreadHandle,
+                                        UserThreadInitiateShutdown,
+                                        &Flags, sizeof(Flags));
+    DPRINT1("Win32k says: %lx\n", Status);
     /* If the message isn't handled, the return value is 0, so 0 doesn't indicate
        success. Success is indicated by a 1 return value, if anything besides 0
@@ -915,6 +938,10 @@
         Status = STATUS_NOT_IMPLEMENTED;
     }
+    DPRINT1("SrvExitWindowsEx returned 0x%08x\n", Status);
+
+Quit:
+    CsrRevertToSelf();
     return Status;
 }
@@ -938,19 +965,42 @@
 CSR_API(SrvExitWindowsEx)
 {
+    NTSTATUS Status;
     PUSER_EXIT_REACTOS ExitReactosRequest =
&((PUSER_API_MESSAGE)ApiMessage)->Data.ExitReactosRequest;
-
-    if (0 == (ExitReactosRequest->Flags & EWX_INTERNAL_FLAG))
-    {
-        return UserExitReactos((DWORD_PTR) ApiMessage->Header.ClientId.UniqueProcess,
-                               ExitReactosRequest->Flags);
+    PCSR_THREAD CsrThread = CsrGetClientThread();
+    ULONG Flags = ExitReactosRequest->Flags;
+
+    /*
+     * Check for flags validity
+     */
+
+    DWORD ProcessId = HandleToUlong(CsrThread->ClientId.UniqueProcess);
+    DWORD ThreadId  = HandleToUlong(CsrThread->ClientId.UniqueThread);
+
+    DPRINT1("SrvExitWindowsEx(ClientId: %lx.%lx, Flags: 0x%x)\n",
+            ProcessId, ThreadId, Flags);
+
+    /* Implicitely add the shutdown flag when we poweroff or reboot */
+    if (Flags & (EWX_POWEROFF | EWX_REBOOT))
+        Flags |= EWX_SHUTDOWN;
+
+
+    // FIXME: Everything else starting after this line is a HAAACK!!
+
+
+    if (0 == (Flags & EWX_INTERNAL_FLAG))
+    {
+        Status = UserExitReactos(CsrThread, Flags);
     }
     else
     {
-        return InternalExitReactos((DWORD_PTR)
ApiMessage->Header.ClientId.UniqueProcess,
-                                   (DWORD_PTR)
ApiMessage->Header.ClientId.UniqueThread,
-                                   ExitReactosRequest->Flags);
-    }
+        // EWX_INTERNAL_FLAG --> For Winlogon only!!
+        // FIXME: This is just a big HAAAACK!!
+        Status = InternalExitReactos(ProcessId, ThreadId, Flags);
+    }
+
+    ExitReactosRequest->Success = NT_SUCCESS(Status);
+    return Status;
 }
 CSR_API(SrvEndTask)