xref: /reactos/base/system/winlogon/sas.c (revision cc3672cb)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Winlogon
4  * FILE:            base/system/winlogon/sas.c
5  * PURPOSE:         Secure Attention Sequence
6  * PROGRAMMERS:     Thomas Weidenmueller (w3seek@users.sourceforge.net)
7  *                  Hervé Poussineau (hpoussin@reactos.org)
8  *                  Arnav Bhatt (arnavbhatt288@gmail.com)
9  * UPDATE HISTORY:
10  *                  Created 28/03/2004
11  */
12 
13 /* INCLUDES *****************************************************************/
14 
15 #include "winlogon.h"
16 
17 #define WIN32_LEAN_AND_MEAN
18 #include <aclapi.h>
19 #include <mmsystem.h>
20 #include <userenv.h>
21 #include <ndk/setypes.h>
22 #include <ndk/sefuncs.h>
23 
24 /* GLOBALS ******************************************************************/
25 
26 #define WINLOGON_SAS_CLASS L"SAS Window class"
27 #define WINLOGON_SAS_TITLE L"SAS window"
28 
29 #define HK_CTRL_ALT_DEL   0
30 #define HK_CTRL_SHIFT_ESC 1
31 
32 // #define EWX_FLAGS_MASK  0x00000014
33 // #define EWX_ACTION_MASK ~EWX_FLAGS_MASK
34 
35 // FIXME: At the moment we use this value (select the lowbyte flags and some highbytes ones).
36 // It should be set such that it makes winlogon accepting only valid flags.
37 #define EWX_ACTION_MASK 0x5C0F
38 
39 typedef struct tagLOGOFF_SHUTDOWN_DATA
40 {
41     UINT Flags;
42     PWLSESSION Session;
43 } LOGOFF_SHUTDOWN_DATA, *PLOGOFF_SHUTDOWN_DATA;
44 
45 static BOOL ExitReactOSInProgress = FALSE;
46 
47 LUID LuidNone = {0, 0};
48 
49 /* FUNCTIONS ****************************************************************/
50 
51 static BOOL
52 StartTaskManager(
53     IN OUT PWLSESSION Session)
54 {
55     LPVOID lpEnvironment;
56     BOOL ret;
57 
58     if (!Session->Gina.Functions.WlxStartApplication)
59         return FALSE;
60 
61     if (!CreateEnvironmentBlock(
62         &lpEnvironment,
63         Session->UserToken,
64         TRUE))
65     {
66         return FALSE;
67     }
68 
69     ret = Session->Gina.Functions.WlxStartApplication(
70         Session->Gina.Context,
71         L"Default",
72         lpEnvironment,
73         L"taskmgr.exe");
74 
75     DestroyEnvironmentBlock(lpEnvironment);
76     return ret;
77 }
78 
79 static BOOL
80 StartUserShell(
81     IN OUT PWLSESSION Session)
82 {
83     LPVOID lpEnvironment = NULL;
84     BOOLEAN Old;
85     BOOL ret;
86 
87     /* Create environment block for the user */
88     if (!CreateEnvironmentBlock(&lpEnvironment, Session->UserToken, TRUE))
89     {
90         WARN("WL: CreateEnvironmentBlock() failed\n");
91         return FALSE;
92     }
93 
94     /* Get privilege */
95     /* FIXME: who should do it? winlogon or gina? */
96     /* FIXME: reverting to lower privileges after creating user shell? */
97     RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &Old);
98 
99     ret = Session->Gina.Functions.WlxActivateUserShell(
100                 Session->Gina.Context,
101                 L"Default",
102                 NULL, /* FIXME */
103                 lpEnvironment);
104 
105     DestroyEnvironmentBlock(lpEnvironment);
106     return ret;
107 }
108 
109 
110 BOOL
111 SetDefaultLanguage(
112     IN PWLSESSION Session)
113 {
114     BOOL ret = FALSE;
115     BOOL UserProfile;
116     LONG rc;
117     HKEY UserKey, hKey = NULL;
118     LPCWSTR SubKey, ValueName;
119     DWORD dwType, dwSize;
120     LPWSTR Value = NULL;
121     UNICODE_STRING ValueString;
122     NTSTATUS Status;
123     LCID Lcid;
124 
125     UserProfile = (Session && Session->UserToken);
126 
127     if (UserProfile && !ImpersonateLoggedOnUser(Session->UserToken))
128     {
129         ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
130         return FALSE;
131         // FIXME: ... or use the default language of the system??
132         // UserProfile = FALSE;
133     }
134 
135     if (UserProfile)
136     {
137         rc = RegOpenCurrentUser(MAXIMUM_ALLOWED, &UserKey);
138         if (rc != ERROR_SUCCESS)
139         {
140             TRACE("RegOpenCurrentUser() failed with error %lu\n", rc);
141             goto cleanup;
142         }
143 
144         SubKey = L"Control Panel\\International";
145         ValueName = L"Locale";
146     }
147     else
148     {
149         UserKey = NULL;
150         SubKey = L"System\\CurrentControlSet\\Control\\Nls\\Language";
151         ValueName = L"Default";
152     }
153 
154     rc = RegOpenKeyExW(UserKey ? UserKey : HKEY_LOCAL_MACHINE,
155                        SubKey,
156                        0,
157                        KEY_READ,
158                        &hKey);
159 
160     if (UserKey)
161         RegCloseKey(UserKey);
162 
163     if (rc != ERROR_SUCCESS)
164     {
165         TRACE("RegOpenKeyEx() failed with error %lu\n", rc);
166         goto cleanup;
167     }
168 
169     rc = RegQueryValueExW(hKey,
170                           ValueName,
171                           NULL,
172                           &dwType,
173                           NULL,
174                           &dwSize);
175     if (rc != ERROR_SUCCESS)
176     {
177         TRACE("RegQueryValueEx() failed with error %lu\n", rc);
178         goto cleanup;
179     }
180     else if (dwType != REG_SZ)
181     {
182         TRACE("Wrong type for %S\\%S registry entry (got 0x%lx, expected 0x%x)\n",
183             SubKey, ValueName, dwType, REG_SZ);
184         goto cleanup;
185     }
186 
187     Value = HeapAlloc(GetProcessHeap(), 0, dwSize);
188     if (!Value)
189     {
190         TRACE("HeapAlloc() failed\n");
191         goto cleanup;
192     }
193     rc = RegQueryValueExW(hKey,
194                           ValueName,
195                           NULL,
196                           NULL,
197                           (LPBYTE)Value,
198                           &dwSize);
199     if (rc != ERROR_SUCCESS)
200     {
201         TRACE("RegQueryValueEx() failed with error %lu\n", rc);
202         goto cleanup;
203     }
204 
205     /* Convert Value to a Lcid */
206     ValueString.Length = ValueString.MaximumLength = (USHORT)dwSize;
207     ValueString.Buffer = Value;
208     Status = RtlUnicodeStringToInteger(&ValueString, 16, (PULONG)&Lcid);
209     if (!NT_SUCCESS(Status))
210     {
211         TRACE("RtlUnicodeStringToInteger() failed with status 0x%08lx\n", Status);
212         goto cleanup;
213     }
214 
215     TRACE("%s language is 0x%08lx\n",
216         UserProfile ? "User" : "System", Lcid);
217     Status = NtSetDefaultLocale(UserProfile, Lcid);
218     if (!NT_SUCCESS(Status))
219     {
220         TRACE("NtSetDefaultLocale() failed with status 0x%08lx\n", Status);
221         goto cleanup;
222     }
223 
224     ret = TRUE;
225 
226 cleanup:
227     if (Value)
228         HeapFree(GetProcessHeap(), 0, Value);
229 
230     if (hKey)
231         RegCloseKey(hKey);
232 
233     if (UserProfile)
234         RevertToSelf();
235 
236     return ret;
237 }
238 
239 BOOL
240 PlaySoundRoutine(
241     IN LPCWSTR FileName,
242     IN UINT bLogon,
243     IN UINT Flags)
244 {
245     typedef BOOL (WINAPI *PLAYSOUNDW)(LPCWSTR,HMODULE,DWORD);
246     typedef UINT (WINAPI *WAVEOUTGETNUMDEVS)(VOID);
247     PLAYSOUNDW Play;
248     WAVEOUTGETNUMDEVS waveOutGetNumDevs;
249     UINT NumDevs;
250     HMODULE hLibrary;
251     BOOL Ret = FALSE;
252 
253     hLibrary = LoadLibraryW(L"winmm.dll");
254     if (!hLibrary)
255         return FALSE;
256 
257     waveOutGetNumDevs = (WAVEOUTGETNUMDEVS)GetProcAddress(hLibrary, "waveOutGetNumDevs");
258     Play = (PLAYSOUNDW)GetProcAddress(hLibrary, "PlaySoundW");
259 
260     _SEH2_TRY
261     {
262         if (waveOutGetNumDevs)
263         {
264             NumDevs = waveOutGetNumDevs();
265             if (!NumDevs)
266             {
267                 if (!bLogon)
268                     Beep(440, 125);
269                 _SEH2_LEAVE;
270             }
271         }
272 
273         if (Play)
274             Ret = Play(FileName, NULL, Flags);
275     }
276     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
277     {
278         ERR("WL: Exception while playing sound '%S', Status 0x%08lx\n",
279             FileName ? FileName : L"(n/a)", _SEH2_GetExceptionCode());
280     }
281     _SEH2_END;
282 
283     FreeLibrary(hLibrary);
284 
285     return Ret;
286 }
287 
288 static
289 BOOL
290 IsFirstLogon(VOID)
291 {
292     /* FIXME: All of this is a HACK, designed specifically for PlayLogonSoundThread.
293      * Don't call IsFirstLogon multiple times inside the same function. And please
294      * note that this function is not thread-safe. */
295     static BOOL bFirstLogon = TRUE;
296     if (bFirstLogon)
297     {
298         bFirstLogon = FALSE;
299         return TRUE;
300     }
301     return FALSE;
302 }
303 
304 DWORD
305 WINAPI
306 PlayLogonSoundThread(
307     IN LPVOID lpParameter)
308 {
309     SERVICE_STATUS_PROCESS Info;
310     DWORD dwSize;
311     ULONG Index = 0;
312     SC_HANDLE hSCManager, hService;
313 
314     /* Open the service manager */
315     hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
316     if (!hSCManager)
317     {
318         ERR("OpenSCManager failed (%x)\n", GetLastError());
319         return 0;
320     }
321 
322     /* Open the wdmaud service */
323     hService = OpenServiceW(hSCManager, L"wdmaud", GENERIC_READ);
324     if (!hService)
325     {
326         /* The service is not installed */
327         TRACE("Failed to open wdmaud service (%x)\n", GetLastError());
328         CloseServiceHandle(hSCManager);
329         return 0;
330     }
331 
332     /* Wait for wdmaud to start */
333     do
334     {
335         if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&Info, sizeof(SERVICE_STATUS_PROCESS), &dwSize))
336         {
337             TRACE("QueryServiceStatusEx failed (%x)\n", GetLastError());
338             break;
339         }
340 
341         if (Info.dwCurrentState == SERVICE_RUNNING)
342             break;
343 
344         Sleep(1000);
345 
346     } while (Index++ < 20);
347 
348     CloseServiceHandle(hService);
349     CloseServiceHandle(hSCManager);
350 
351     /* If wdmaud is not running exit */
352     if (Info.dwCurrentState != SERVICE_RUNNING)
353     {
354         WARN("wdmaud has not started!\n");
355         return 0;
356     }
357 
358     /* Sound subsystem is running. Play logon sound. */
359     TRACE("Playing logon sound\n");
360     if (!ImpersonateLoggedOnUser((HANDLE)lpParameter))
361     {
362         ERR("ImpersonateLoggedOnUser failed (%x)\n", GetLastError());
363     }
364     else
365     {
366         PlaySoundRoutine(IsFirstLogon() ? L"SystemStart" : L"WindowsLogon",
367                          TRUE,
368                          SND_ALIAS | SND_NODEFAULT);
369         RevertToSelf();
370     }
371     return 0;
372 }
373 
374 static
375 VOID
376 PlayLogonSound(
377     IN OUT PWLSESSION Session)
378 {
379     HANDLE hThread;
380 
381     hThread = CreateThread(NULL, 0, PlayLogonSoundThread, (PVOID)Session->UserToken, 0, NULL);
382     if (hThread)
383         CloseHandle(hThread);
384 }
385 
386 static
387 VOID
388 PlayLogoffShutdownSound(
389     _In_ PWLSESSION Session,
390     _In_ BOOL bShutdown)
391 {
392     if (!ImpersonateLoggedOnUser(Session->UserToken))
393         return;
394 
395     /* NOTE: Logoff and shutdown sounds play synchronously */
396     PlaySoundRoutine(bShutdown ? L"SystemExit" : L"WindowsLogoff",
397                      FALSE,
398                      SND_ALIAS | SND_NODEFAULT);
399 
400     RevertToSelf();
401 }
402 
403 static
404 BOOL
405 PlayEventSound(
406     _In_ PWLSESSION Session,
407     _In_ LPCWSTR EventName)
408 {
409     BOOL bRet;
410 
411     if (!ImpersonateLoggedOnUser(Session->UserToken))
412         return FALSE;
413 
414     bRet = PlaySoundRoutine(EventName, FALSE, SND_ALIAS | SND_ASYNC | SND_NODEFAULT);
415 
416     RevertToSelf();
417 
418     return bRet;
419 }
420 
421 static
422 VOID
423 RestoreAllConnections(PWLSESSION Session)
424 {
425     DWORD dRet;
426     HANDLE hEnum;
427     LPNETRESOURCE lpRes;
428     DWORD dSize = 0x1000;
429     DWORD dCount = -1;
430     LPNETRESOURCE lpCur;
431     BOOL UserProfile;
432 
433     UserProfile = (Session && Session->UserToken);
434     if (!UserProfile)
435     {
436         return;
437     }
438 
439     if (!ImpersonateLoggedOnUser(Session->UserToken))
440     {
441         ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
442         return;
443     }
444 
445     dRet = WNetOpenEnum(RESOURCE_REMEMBERED, RESOURCETYPE_DISK, 0, NULL, &hEnum);
446     if (dRet != WN_SUCCESS)
447     {
448         ERR("Failed to open enumeration: %lu\n", dRet);
449         goto quit;
450     }
451 
452     lpRes = HeapAlloc(GetProcessHeap(), 0, dSize);
453     if (!lpRes)
454     {
455         ERR("Failed to allocate memory\n");
456         WNetCloseEnum(hEnum);
457         goto quit;
458     }
459 
460     do
461     {
462         dSize = 0x1000;
463         dCount = -1;
464 
465         memset(lpRes, 0, dSize);
466         dRet = WNetEnumResource(hEnum, &dCount, lpRes, &dSize);
467         if (dRet == WN_SUCCESS || dRet == WN_MORE_DATA)
468         {
469             lpCur = lpRes;
470             for (; dCount; dCount--)
471             {
472                 WNetAddConnection(lpCur->lpRemoteName, NULL, lpCur->lpLocalName);
473                 lpCur++;
474             }
475         }
476     } while (dRet != WN_NO_MORE_ENTRIES);
477 
478     HeapFree(GetProcessHeap(), 0, lpRes);
479     WNetCloseEnum(hEnum);
480 
481 quit:
482     RevertToSelf();
483 }
484 
485 static
486 BOOL
487 HandleLogon(
488     IN OUT PWLSESSION Session)
489 {
490     PROFILEINFOW ProfileInfo;
491     BOOL ret = FALSE;
492 
493     /* Loading personal settings */
494     DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOADINGYOURPERSONALSETTINGS);
495     ProfileInfo.hProfile = INVALID_HANDLE_VALUE;
496     if (0 == (Session->Options & WLX_LOGON_OPT_NO_PROFILE))
497     {
498         if (Session->Profile == NULL
499          || (Session->Profile->dwType != WLX_PROFILE_TYPE_V1_0
500           && Session->Profile->dwType != WLX_PROFILE_TYPE_V2_0))
501         {
502             ERR("WL: Wrong profile\n");
503             goto cleanup;
504         }
505 
506         /* Load the user profile */
507         ZeroMemory(&ProfileInfo, sizeof(PROFILEINFOW));
508         ProfileInfo.dwSize = sizeof(PROFILEINFOW);
509         ProfileInfo.dwFlags = 0;
510         ProfileInfo.lpUserName = Session->MprNotifyInfo.pszUserName;
511         ProfileInfo.lpProfilePath = Session->Profile->pszProfile;
512         if (Session->Profile->dwType >= WLX_PROFILE_TYPE_V2_0)
513         {
514             ProfileInfo.lpDefaultPath = Session->Profile->pszNetworkDefaultUserProfile;
515             ProfileInfo.lpServerName = Session->Profile->pszServerName;
516             ProfileInfo.lpPolicyPath = Session->Profile->pszPolicy;
517         }
518 
519         if (!LoadUserProfileW(Session->UserToken, &ProfileInfo))
520         {
521             ERR("WL: LoadUserProfileW() failed\n");
522             goto cleanup;
523         }
524     }
525 
526     /* Create environment block for the user */
527     if (!CreateUserEnvironment(Session))
528     {
529         WARN("WL: SetUserEnvironment() failed\n");
530         goto cleanup;
531     }
532 
533     CallNotificationDlls(Session, LogonHandler);
534 
535     DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_APPLYINGYOURPERSONALSETTINGS);
536     UpdatePerUserSystemParameters(0, TRUE);
537 
538     /* Set default user language */
539     if (!SetDefaultLanguage(Session))
540     {
541         WARN("WL: SetDefaultLanguage() failed\n");
542         goto cleanup;
543     }
544 
545     /* Allow winsta and desktop access for this session */
546     if (!AllowAccessOnSession(Session))
547     {
548         WARN("WL: AllowAccessOnSession() failed to give winsta & desktop access for this session\n");
549         goto cleanup;
550     }
551 
552     /* Connect remote resources */
553     RestoreAllConnections(Session);
554 
555     if (!StartUserShell(Session))
556     {
557         //WCHAR StatusMsg[256];
558         WARN("WL: WlxActivateUserShell() failed\n");
559         //LoadStringW(hAppInstance, IDS_FAILEDACTIVATEUSERSHELL, StatusMsg, sizeof(StatusMsg) / sizeof(StatusMsg[0]));
560         //MessageBoxW(0, StatusMsg, NULL, MB_ICONERROR);
561         goto cleanup;
562     }
563 
564     CallNotificationDlls(Session, StartShellHandler);
565 
566     if (!InitializeScreenSaver(Session))
567         WARN("WL: Failed to initialize screen saver\n");
568 
569     Session->hProfileInfo = ProfileInfo.hProfile;
570 
571     /* Logon has succeeded. Play sound. */
572     PlayLogonSound(Session);
573 
574     ret = TRUE;
575 
576 cleanup:
577     if (Session->Profile)
578     {
579         HeapFree(GetProcessHeap(), 0, Session->Profile->pszProfile);
580         HeapFree(GetProcessHeap(), 0, Session->Profile);
581     }
582     Session->Profile = NULL;
583     if (!ret && ProfileInfo.hProfile != INVALID_HANDLE_VALUE)
584     {
585         UnloadUserProfile(Session->UserToken, ProfileInfo.hProfile);
586     }
587     RemoveStatusMessage(Session);
588     if (!ret)
589     {
590         SetWindowStationUser(Session->InteractiveWindowStation,
591                              &LuidNone, NULL, 0);
592         CloseHandle(Session->UserToken);
593         Session->UserToken = NULL;
594     }
595 
596     if (ret)
597     {
598         SwitchDesktop(Session->ApplicationDesktop);
599         Session->LogonState = STATE_LOGGED_ON;
600     }
601 
602     return ret;
603 }
604 
605 
606 static
607 DWORD
608 WINAPI
609 LogoffShutdownThread(
610     LPVOID Parameter)
611 {
612     DWORD ret = 1;
613     PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA)Parameter;
614     UINT uFlags;
615 
616     if (LSData->Session->UserToken != NULL &&
617         !ImpersonateLoggedOnUser(LSData->Session->UserToken))
618     {
619         ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
620         return 0;
621     }
622 
623     // FIXME: To be really fixed: need to check what needs to be kept and what needs to be removed there.
624     //
625     // uFlags = EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) |
626              // ((LSData->Flags & EWX_ACTION_MASK) == EWX_LOGOFF ? EWX_CALLER_WINLOGON_LOGOFF : 0);
627 
628     uFlags = EWX_CALLER_WINLOGON | (LSData->Flags & 0x0F);
629 
630     TRACE("In LogoffShutdownThread with uFlags == 0x%x; exit_in_progress == %s\n",
631         uFlags, ExitReactOSInProgress ? "true" : "false");
632 
633     ExitReactOSInProgress = TRUE;
634 
635     /* Close processes of the interactive user */
636     if (!ExitWindowsEx(uFlags, 0))
637     {
638         ERR("Unable to kill user apps, error %lu\n", GetLastError());
639         ret = 0;
640     }
641 
642     /* Cancel all the user connections */
643     WNetClearConnections(NULL);
644 
645     if (LSData->Session->UserToken)
646         RevertToSelf();
647 
648     return ret;
649 }
650 
651 static
652 DWORD
653 WINAPI
654 KillComProcesses(
655     LPVOID Parameter)
656 {
657     DWORD ret = 1;
658     PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA)Parameter;
659 
660     TRACE("In KillComProcesses\n");
661 
662     if (LSData->Session->UserToken != NULL &&
663         !ImpersonateLoggedOnUser(LSData->Session->UserToken))
664     {
665         ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
666         return 0;
667     }
668 
669     /* Attempt to kill remaining processes. No notifications needed. */
670     if (!ExitWindowsEx(EWX_CALLER_WINLOGON | EWX_NONOTIFY | EWX_FORCE | EWX_LOGOFF, 0))
671     {
672         ERR("Unable to kill COM apps, error %lu\n", GetLastError());
673         ret = 0;
674     }
675 
676     if (LSData->Session->UserToken)
677         RevertToSelf();
678 
679     return ret;
680 }
681 
682 static
683 NTSTATUS
684 CreateLogoffSecurityAttributes(
685     OUT PSECURITY_ATTRIBUTES* ppsa)
686 {
687     /* The following code is not working yet and messy */
688     /* Still, it gives some ideas about data types and functions involved and */
689     /* required to set up a SECURITY_DESCRIPTOR for a SECURITY_ATTRIBUTES */
690     /* instance for a thread, to allow that  thread to ImpersonateLoggedOnUser(). */
691     /* Specifically THREAD_SET_THREAD_TOKEN is required. */
692     PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
693     PSECURITY_ATTRIBUTES psa = 0;
694     BYTE* pMem;
695     PACL pACL;
696     EXPLICIT_ACCESS Access;
697     PSID pEveryoneSID = NULL;
698     static SID_IDENTIFIER_AUTHORITY WorldAuthority = { SECURITY_WORLD_SID_AUTHORITY };
699 
700     *ppsa = NULL;
701 
702     // Let's first try to enumerate what kind of data we need for this to ever work:
703     // 1.  The Winlogon SID, to be able to give it THREAD_SET_THREAD_TOKEN.
704     // 2.  The users SID (the user trying to logoff, or rather shut down the system).
705     // 3.  At least two EXPLICIT_ACCESS instances:
706     // 3.1 One for Winlogon itself, giving it the rights
707     //     required to THREAD_SET_THREAD_TOKEN (as it's needed to successfully call
708     //     ImpersonateLoggedOnUser).
709     // 3.2 One for the user, to allow *that* thread to perform its work.
710     // 4.  An ACL to hold the these EXPLICIT_ACCESS ACE's.
711     // 5.  A SECURITY_DESCRIPTOR to hold the ACL, and finally.
712     // 6.  A SECURITY_ATTRIBUTES instance to pull all of this required stuff
713     //     together, to hand it to CreateThread.
714     //
715     // However, it seems struct LOGOFF_SHUTDOWN_DATA doesn't contain
716     // these required SID's, why they'd have to be added.
717     // The Winlogon's own SID should probably only be created once,
718     // while the user's SID obviously must be created for each new user.
719     // Might as well store it when the user logs on?
720 
721     if(!AllocateAndInitializeSid(&WorldAuthority,
722                                  1,
723                                  SECURITY_WORLD_RID,
724                                  0, 0, 0, 0, 0, 0, 0,
725                                  &pEveryoneSID))
726     {
727         ERR("Failed to initialize security descriptor for logoff thread!\n");
728         return STATUS_UNSUCCESSFUL;
729     }
730 
731     /* set up the required security attributes to be able to shut down */
732     /* To save space and time, allocate a single block of memory holding */
733     /* both SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR */
734     pMem = HeapAlloc(GetProcessHeap(),
735                      0,
736                      sizeof(SECURITY_ATTRIBUTES) +
737                      SECURITY_DESCRIPTOR_MIN_LENGTH +
738                      sizeof(ACL));
739     if (!pMem)
740     {
741         ERR("Failed to allocate memory for logoff security descriptor!\n");
742         return STATUS_NO_MEMORY;
743     }
744 
745     /* Note that the security descriptor needs to be in _absolute_ format, */
746     /* meaning its members must be pointers to other structures, rather */
747     /* than the relative format using offsets */
748     psa = (PSECURITY_ATTRIBUTES)pMem;
749     SecurityDescriptor = (PSECURITY_DESCRIPTOR)(pMem + sizeof(SECURITY_ATTRIBUTES));
750     pACL = (PACL)(((PBYTE)SecurityDescriptor) + SECURITY_DESCRIPTOR_MIN_LENGTH);
751 
752     // Initialize an EXPLICIT_ACCESS structure for an ACE.
753     // The ACE will allow this thread to log off (and shut down the system, currently).
754     ZeroMemory(&Access, sizeof(Access));
755     Access.grfAccessPermissions = THREAD_SET_THREAD_TOKEN;
756     Access.grfAccessMode = SET_ACCESS; // GRANT_ACCESS?
757     Access.grfInheritance = NO_INHERITANCE;
758     Access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
759     Access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
760     Access.Trustee.ptstrName = pEveryoneSID;
761 
762     if (SetEntriesInAcl(1, &Access, NULL, &pACL) != ERROR_SUCCESS)
763     {
764         ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n");
765 
766         HeapFree(GetProcessHeap(), 0, pMem);
767         return STATUS_UNSUCCESSFUL;
768     }
769 
770     if (!InitializeSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
771     {
772         ERR("Failed to initialize security descriptor for logoff thread!\n");
773         HeapFree(GetProcessHeap(), 0, pMem);
774         return STATUS_UNSUCCESSFUL;
775     }
776 
777     if (!SetSecurityDescriptorDacl(SecurityDescriptor,
778                                    TRUE,     // bDaclPresent flag
779                                    pACL,
780                                    FALSE))   // not a default DACL
781     {
782         ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
783         HeapFree(GetProcessHeap(), 0, pMem);
784         return STATUS_UNSUCCESSFUL;
785     }
786 
787     psa->nLength = sizeof(SECURITY_ATTRIBUTES);
788     psa->lpSecurityDescriptor = SecurityDescriptor;
789     psa->bInheritHandle = FALSE;
790 
791     *ppsa = psa;
792 
793     return STATUS_SUCCESS;
794 }
795 
796 static
797 VOID
798 DestroyLogoffSecurityAttributes(
799     IN PSECURITY_ATTRIBUTES psa)
800 {
801     if (psa)
802     {
803         HeapFree(GetProcessHeap(), 0, psa);
804     }
805 }
806 
807 
808 static
809 NTSTATUS
810 HandleLogoff(
811     _Inout_ PWLSESSION Session,
812     _In_ DWORD wlxAction)
813 {
814     PLOGOFF_SHUTDOWN_DATA LSData;
815     PSECURITY_ATTRIBUTES psa;
816     HANDLE hThread;
817     DWORD exitCode;
818     NTSTATUS Status;
819 
820     /* Prepare data for logoff thread */
821     LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
822     if (!LSData)
823     {
824         ERR("Failed to allocate mem for thread data\n");
825         return STATUS_NO_MEMORY;
826     }
827 
828     LSData->Flags = EWX_LOGOFF;
829     if (wlxAction == WLX_SAS_ACTION_FORCE_LOGOFF)
830     {
831         LSData->Flags |= EWX_FORCE;
832     }
833 
834     LSData->Session = Session;
835 
836     Status = CreateLogoffSecurityAttributes(&psa);
837     if (!NT_SUCCESS(Status))
838     {
839         ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status);
840         HeapFree(GetProcessHeap(), 0, LSData);
841         return Status;
842     }
843 
844     /* Run logoff thread */
845     hThread = CreateThread(psa, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
846     if (!hThread)
847     {
848         ERR("Unable to create logoff thread, error %lu\n", GetLastError());
849         DestroyLogoffSecurityAttributes(psa);
850         HeapFree(GetProcessHeap(), 0, LSData);
851         return STATUS_UNSUCCESSFUL;
852     }
853     WaitForSingleObject(hThread, INFINITE);
854     if (!GetExitCodeThread(hThread, &exitCode))
855     {
856         ERR("Unable to get exit code of logoff thread (error %lu)\n", GetLastError());
857         CloseHandle(hThread);
858         DestroyLogoffSecurityAttributes(psa);
859         HeapFree(GetProcessHeap(), 0, LSData);
860         return STATUS_UNSUCCESSFUL;
861     }
862     CloseHandle(hThread);
863     if (exitCode == 0)
864     {
865         ERR("Logoff thread returned failure\n");
866         DestroyLogoffSecurityAttributes(psa);
867         HeapFree(GetProcessHeap(), 0, LSData);
868         return STATUS_UNSUCCESSFUL;
869     }
870 
871     SwitchDesktop(Session->WinlogonDesktop);
872 
873     PlayLogoffShutdownSound(Session, WLX_SHUTTINGDOWN(wlxAction));
874 
875     SetWindowStationUser(Session->InteractiveWindowStation,
876                          &LuidNone, NULL, 0);
877 
878     // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOGGINGOFF);
879 
880     // FIXME: Closing network connections!
881     // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_CLOSINGNETWORKCONNECTIONS);
882 
883     /* Kill remaining COM apps. Only at logoff! */
884     hThread = CreateThread(psa, 0, KillComProcesses, (LPVOID)LSData, 0, NULL);
885     if (hThread)
886     {
887         WaitForSingleObject(hThread, INFINITE);
888         CloseHandle(hThread);
889     }
890 
891     /* We're done with the SECURITY_DESCRIPTOR */
892     DestroyLogoffSecurityAttributes(psa);
893     psa = NULL;
894 
895     HeapFree(GetProcessHeap(), 0, LSData);
896 
897     DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_SAVEYOURSETTINGS);
898 
899     UnloadUserProfile(Session->UserToken, Session->hProfileInfo);
900 
901     CallNotificationDlls(Session, LogoffHandler);
902 
903     CloseHandle(Session->UserToken);
904     UpdatePerUserSystemParameters(0, FALSE);
905     Session->LogonState = STATE_LOGGED_OFF;
906     Session->UserToken = NULL;
907 
908     return STATUS_SUCCESS;
909 }
910 
911 static
912 INT_PTR
913 CALLBACK
914 ShutdownComputerWindowProc(
915     IN HWND hwndDlg,
916     IN UINT uMsg,
917     IN WPARAM wParam,
918     IN LPARAM lParam)
919 {
920     UNREFERENCED_PARAMETER(lParam);
921 
922     switch (uMsg)
923     {
924         case WM_COMMAND:
925         {
926             switch (LOWORD(wParam))
927             {
928                 case IDC_BTNSHTDOWNCOMPUTER:
929                     EndDialog(hwndDlg, IDC_BTNSHTDOWNCOMPUTER);
930                     return TRUE;
931             }
932             break;
933         }
934         case WM_INITDIALOG:
935         {
936             RemoveMenu(GetSystemMenu(hwndDlg, FALSE), SC_CLOSE, MF_BYCOMMAND);
937             SetFocus(GetDlgItem(hwndDlg, IDC_BTNSHTDOWNCOMPUTER));
938             return TRUE;
939         }
940     }
941     return FALSE;
942 }
943 
944 static
945 VOID
946 UninitializeSAS(
947     IN OUT PWLSESSION Session)
948 {
949     if (Session->SASWindow)
950     {
951         DestroyWindow(Session->SASWindow);
952         Session->SASWindow = NULL;
953     }
954     if (Session->hEndOfScreenSaverThread)
955         SetEvent(Session->hEndOfScreenSaverThread);
956     UnregisterClassW(WINLOGON_SAS_CLASS, hAppInstance);
957 }
958 
959 NTSTATUS
960 HandleShutdown(
961     IN OUT PWLSESSION Session,
962     IN DWORD wlxAction)
963 {
964     PLOGOFF_SHUTDOWN_DATA LSData;
965     HANDLE hThread;
966     DWORD exitCode;
967     BOOLEAN Old;
968 
969     // SwitchDesktop(Session->WinlogonDesktop);
970 
971     /* If the system is rebooting, show the appropriate string */
972     if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
973         DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_REACTOSISRESTARTING);
974     else
975         DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_REACTOSISSHUTTINGDOWN);
976 
977     /* Prepare data for shutdown thread */
978     LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
979     if (!LSData)
980     {
981         ERR("Failed to allocate mem for thread data\n");
982         return STATUS_NO_MEMORY;
983     }
984     if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF)
985         LSData->Flags = EWX_POWEROFF;
986     else if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
987         LSData->Flags = EWX_REBOOT;
988     else
989         LSData->Flags = EWX_SHUTDOWN;
990     LSData->Session = Session;
991 
992     // FIXME: We may need to specify this flag to really force application kill
993     // (we are shutting down ReactOS, not just logging off so no hangs, etc...
994     // should be allowed).
995     // LSData->Flags |= EWX_FORCE;
996 
997     /* Run shutdown thread */
998     hThread = CreateThread(NULL, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
999     if (!hThread)
1000     {
1001         ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
1002         HeapFree(GetProcessHeap(), 0, LSData);
1003         return STATUS_UNSUCCESSFUL;
1004     }
1005     WaitForSingleObject(hThread, INFINITE);
1006     HeapFree(GetProcessHeap(), 0, LSData);
1007     if (!GetExitCodeThread(hThread, &exitCode))
1008     {
1009         ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError());
1010         CloseHandle(hThread);
1011         return STATUS_UNSUCCESSFUL;
1012     }
1013     CloseHandle(hThread);
1014     if (exitCode == 0)
1015     {
1016         ERR("Shutdown thread returned failure\n");
1017         return STATUS_UNSUCCESSFUL;
1018     }
1019 
1020     CallNotificationDlls(Session, ShutdownHandler);
1021 
1022     /* Destroy SAS window */
1023     UninitializeSAS(Session);
1024 
1025     /* Now we can shut down NT */
1026     ERR("Shutting down NT...\n");
1027     RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old);
1028     if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
1029     {
1030         NtShutdownSystem(ShutdownReboot);
1031     }
1032     else
1033     {
1034         if (FALSE)
1035         {
1036             /* FIXME - only show this dialog if it's a shutdown and the computer doesn't support APM */
1037             DialogBox(hAppInstance, MAKEINTRESOURCE(IDD_SHUTDOWNCOMPUTER),
1038                       GetDesktopWindow(), ShutdownComputerWindowProc);
1039         }
1040         NtShutdownSystem(ShutdownNoReboot);
1041     }
1042     RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, Old, FALSE, &Old);
1043     return STATUS_SUCCESS;
1044 }
1045 
1046 static
1047 VOID
1048 DoGenericAction(
1049     IN OUT PWLSESSION Session,
1050     IN DWORD wlxAction)
1051 {
1052     switch (wlxAction)
1053     {
1054         case WLX_SAS_ACTION_LOGON: /* 0x01 */
1055             if (Session->LogonState == STATE_LOGGED_OFF_SAS)
1056             {
1057                 if (!HandleLogon(Session))
1058                 {
1059                     Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
1060                     CallNotificationDlls(Session, LogonHandler);
1061                 }
1062             }
1063             break;
1064         case WLX_SAS_ACTION_NONE: /* 0x02 */
1065             if (Session->LogonState == STATE_LOGGED_OFF_SAS)
1066             {
1067                 Session->LogonState = STATE_LOGGED_OFF;
1068                 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
1069             }
1070             else if (Session->LogonState == STATE_LOGGED_ON_SAS)
1071             {
1072                 Session->LogonState = STATE_LOGGED_ON;
1073             }
1074             else if (Session->LogonState == STATE_LOCKED_SAS)
1075             {
1076                 Session->LogonState = STATE_LOCKED;
1077                 Session->Gina.Functions.WlxDisplayLockedNotice(Session->Gina.Context);
1078             }
1079             break;
1080         case WLX_SAS_ACTION_LOCK_WKSTA: /* 0x03 */
1081             if (Session->Gina.Functions.WlxIsLockOk(Session->Gina.Context))
1082             {
1083                 SwitchDesktop(Session->WinlogonDesktop);
1084                 Session->LogonState = STATE_LOCKED;
1085                 Session->Gina.Functions.WlxDisplayLockedNotice(Session->Gina.Context);
1086                 CallNotificationDlls(Session, LockHandler);
1087             }
1088             break;
1089         case WLX_SAS_ACTION_LOGOFF: /* 0x04 */
1090         case WLX_SAS_ACTION_SHUTDOWN: /* 0x05 */
1091         case WLX_SAS_ACTION_FORCE_LOGOFF: /* 0x09 */
1092         case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF: /* 0x0a */
1093         case WLX_SAS_ACTION_SHUTDOWN_REBOOT: /* 0x0b */
1094             if (Session->LogonState != STATE_LOGGED_OFF)
1095             {
1096                 if (!Session->Gina.Functions.WlxIsLogoffOk(Session->Gina.Context))
1097                     break;
1098                 if (!NT_SUCCESS(HandleLogoff(Session, wlxAction)))
1099                 {
1100                     RemoveStatusMessage(Session);
1101                     break;
1102                 }
1103                 Session->Gina.Functions.WlxLogoff(Session->Gina.Context);
1104             }
1105             if (WLX_SHUTTINGDOWN(wlxAction))
1106             {
1107                 // FIXME: WlxShutdown should be done from inside HandleShutdown,
1108                 // after having displayed "ReactOS is shutting down" message.
1109                 Session->Gina.Functions.WlxShutdown(Session->Gina.Context, wlxAction);
1110                 if (!NT_SUCCESS(HandleShutdown(Session, wlxAction)))
1111                 {
1112                     RemoveStatusMessage(Session);
1113                     Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
1114                 }
1115             }
1116             else
1117             {
1118                 RemoveStatusMessage(Session);
1119                 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
1120             }
1121             break;
1122         case WLX_SAS_ACTION_TASKLIST: /* 0x07 */
1123             SwitchDesktop(Session->ApplicationDesktop);
1124             Session->LogonState = STATE_LOGGED_ON;
1125             StartTaskManager(Session);
1126             break;
1127         case WLX_SAS_ACTION_UNLOCK_WKSTA: /* 0x08 */
1128             SwitchDesktop(Session->ApplicationDesktop);
1129             Session->LogonState = STATE_LOGGED_ON;
1130             CallNotificationDlls(Session, UnlockHandler);
1131             break;
1132         default:
1133             WARN("Unknown SAS action 0x%lx\n", wlxAction);
1134     }
1135 }
1136 
1137 static
1138 VOID
1139 DispatchSAS(
1140     IN OUT PWLSESSION Session,
1141     IN DWORD dwSasType)
1142 {
1143     DWORD wlxAction = WLX_SAS_ACTION_NONE;
1144     PSID LogonSid = NULL; /* FIXME */
1145     BOOL bSecure = TRUE;
1146 
1147     switch (dwSasType)
1148     {
1149         case WLX_SAS_TYPE_CTRL_ALT_DEL:
1150             switch (Session->LogonState)
1151             {
1152                 case STATE_INIT:
1153                     Session->LogonState = STATE_LOGGED_OFF;
1154                     Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
1155                     return;
1156 
1157                 case STATE_LOGGED_OFF:
1158                     Session->LogonState = STATE_LOGGED_OFF_SAS;
1159 
1160                     CloseAllDialogWindows();
1161 
1162                     Session->Options = 0;
1163 
1164                     wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOutSAS(
1165                         Session->Gina.Context,
1166                         Session->SASAction,
1167                         &Session->LogonId,
1168                         LogonSid,
1169                         &Session->Options,
1170                         &Session->UserToken,
1171                         &Session->MprNotifyInfo,
1172                         (PVOID*)&Session->Profile);
1173                     break;
1174 
1175                 case STATE_LOGGED_OFF_SAS:
1176                     /* Ignore SAS if we are already in an SAS state */
1177                     return;
1178 
1179                 case STATE_LOGGED_ON:
1180                     Session->LogonState = STATE_LOGGED_ON_SAS;
1181                     wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOnSAS(Session->Gina.Context, dwSasType, NULL);
1182                     break;
1183 
1184                 case STATE_LOGGED_ON_SAS:
1185                     /* Ignore SAS if we are already in an SAS state */
1186                     return;
1187 
1188                 case STATE_LOCKED:
1189                     Session->LogonState = STATE_LOCKED_SAS;
1190 
1191                     CloseAllDialogWindows();
1192 
1193                     wlxAction = (DWORD)Session->Gina.Functions.WlxWkstaLockedSAS(Session->Gina.Context, dwSasType);
1194                     break;
1195 
1196                 case STATE_LOCKED_SAS:
1197                     /* Ignore SAS if we are already in an SAS state */
1198                     return;
1199 
1200                 default:
1201                     return;
1202             }
1203             break;
1204 
1205         case WLX_SAS_TYPE_TIMEOUT:
1206             return;
1207 
1208         case WLX_SAS_TYPE_SCRNSVR_TIMEOUT:
1209             if (!Session->Gina.Functions.WlxScreenSaverNotify(Session->Gina.Context, &bSecure))
1210             {
1211                 /* Skip start of screen saver */
1212                 SetEvent(Session->hEndOfScreenSaver);
1213             }
1214             else
1215             {
1216                 StartScreenSaver(Session);
1217                 if (bSecure)
1218                 {
1219                     wlxAction = WLX_SAS_ACTION_LOCK_WKSTA;
1220 //                    DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
1221                 }
1222             }
1223             break;
1224 
1225         case WLX_SAS_TYPE_SCRNSVR_ACTIVITY:
1226             SetEvent(Session->hUserActivity);
1227             break;
1228     }
1229 
1230     DoGenericAction(Session, wlxAction);
1231 }
1232 
1233 static
1234 BOOL
1235 RegisterHotKeys(
1236     IN PWLSESSION Session,
1237     IN HWND hwndSAS)
1238 {
1239     /* Register Ctrl+Alt+Del Hotkey */
1240     if (!RegisterHotKey(hwndSAS, HK_CTRL_ALT_DEL, MOD_CONTROL | MOD_ALT, VK_DELETE))
1241     {
1242         ERR("WL: Unable to register Ctrl+Alt+Del hotkey!\n");
1243         return FALSE;
1244     }
1245 
1246     /* Register Ctrl+Shift+Esc (optional) */
1247     Session->TaskManHotkey = RegisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC, MOD_CONTROL | MOD_SHIFT, VK_ESCAPE);
1248     if (!Session->TaskManHotkey)
1249         WARN("WL: Warning: Unable to register Ctrl+Alt+Esc hotkey!\n");
1250     return TRUE;
1251 }
1252 
1253 static
1254 BOOL
1255 UnregisterHotKeys(
1256     IN PWLSESSION Session,
1257     IN HWND hwndSAS)
1258 {
1259     /* Unregister hotkeys */
1260     UnregisterHotKey(hwndSAS, HK_CTRL_ALT_DEL);
1261 
1262     if (Session->TaskManHotkey)
1263         UnregisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC);
1264 
1265     return TRUE;
1266 }
1267 
1268 static
1269 BOOL
1270 HandleMessageBeep(
1271     _In_ PWLSESSION Session,
1272     _In_ UINT uType)
1273 {
1274     LPWSTR EventName;
1275 
1276     switch (uType)
1277     {
1278         case 0xFFFFFFFF:
1279             EventName = NULL;
1280             break;
1281         case MB_OK:
1282             EventName = L"SystemDefault";
1283             break;
1284         case MB_ICONASTERISK:
1285             EventName = L"SystemAsterisk";
1286             break;
1287         case MB_ICONEXCLAMATION:
1288             EventName = L"SystemExclamation";
1289             break;
1290         case MB_ICONHAND:
1291             EventName = L"SystemHand";
1292             break;
1293         case MB_ICONQUESTION:
1294             EventName = L"SystemQuestion";
1295             break;
1296         default:
1297             WARN("Unhandled type %d\n", uType);
1298             EventName = L"SystemDefault";
1299     }
1300 
1301     return PlayEventSound(Session, EventName);
1302 }
1303 
1304 static
1305 LRESULT
1306 CALLBACK
1307 SASWindowProc(
1308     IN HWND hwndDlg,
1309     IN UINT uMsg,
1310     IN WPARAM wParam,
1311     IN LPARAM lParam)
1312 {
1313     PWLSESSION Session = (PWLSESSION)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1314 
1315     switch (uMsg)
1316     {
1317         case WM_HOTKEY:
1318         {
1319             switch (lParam)
1320             {
1321                 case MAKELONG(MOD_CONTROL | MOD_ALT, VK_DELETE):
1322                 {
1323                     TRACE("SAS: CONTROL+ALT+DELETE\n");
1324                     if (!Session->Gina.UseCtrlAltDelete)
1325                         break;
1326                     PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_CTRL_ALT_DEL, 0);
1327                     return TRUE;
1328                 }
1329                 case MAKELONG(MOD_CONTROL | MOD_SHIFT, VK_ESCAPE):
1330                 {
1331                     TRACE("SAS: CONTROL+SHIFT+ESCAPE\n");
1332                     if (Session->LogonState == STATE_LOGGED_ON)
1333                         DoGenericAction(Session, WLX_SAS_ACTION_TASKLIST);
1334                     return TRUE;
1335                 }
1336             }
1337             break;
1338         }
1339         case WM_CREATE:
1340         {
1341             /* Get the session pointer from the create data */
1342             Session = (PWLSESSION)((LPCREATESTRUCT)lParam)->lpCreateParams;
1343 
1344             /* Save the Session pointer */
1345             SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)Session);
1346             if (GetSetupType())
1347                 return TRUE;
1348             return RegisterHotKeys(Session, hwndDlg);
1349         }
1350         case WM_DESTROY:
1351         {
1352             if (!GetSetupType())
1353                 UnregisterHotKeys(Session, hwndDlg);
1354             return TRUE;
1355         }
1356         case WM_SETTINGCHANGE:
1357         {
1358             UINT uiAction = (UINT)wParam;
1359             if (uiAction == SPI_SETSCREENSAVETIMEOUT
1360              || uiAction == SPI_SETSCREENSAVEACTIVE)
1361             {
1362                 SetEvent(Session->hScreenSaverParametersChanged);
1363             }
1364             return TRUE;
1365         }
1366         case WM_LOGONNOTIFY:
1367         {
1368             switch(wParam)
1369             {
1370                 case LN_MESSAGE_BEEP:
1371                 {
1372                     return HandleMessageBeep(Session, lParam);
1373                 }
1374                 case LN_SHELL_EXITED:
1375                 {
1376                     /* lParam is the exit code */
1377                     if (lParam != 1 &&
1378                         Session->LogonState != STATE_LOGGED_OFF &&
1379                         Session->LogonState != STATE_LOGGED_OFF_SAS)
1380                     {
1381                         SetTimer(hwndDlg, 1, 1000, NULL);
1382                     }
1383                     break;
1384                 }
1385                 case LN_START_SCREENSAVE:
1386                 {
1387                     DispatchSAS(Session, WLX_SAS_TYPE_SCRNSVR_TIMEOUT);
1388                     break;
1389                 }
1390                 case LN_LOCK_WORKSTATION:
1391                 {
1392                     DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
1393                     break;
1394                 }
1395                 case LN_LOGOFF:
1396                 {
1397                     UINT Flags = (UINT)lParam;
1398                     UINT Action = Flags & EWX_ACTION_MASK;
1399                     DWORD wlxAction;
1400 
1401                     TRACE("\tFlags : 0x%lx\n", lParam);
1402 
1403                     /*
1404                      * Our caller (USERSRV) should have added the shutdown flag
1405                      * when setting also poweroff or reboot.
1406                      */
1407                     if (Action & (EWX_POWEROFF | EWX_REBOOT))
1408                     {
1409                         if ((Action & EWX_SHUTDOWN) == 0)
1410                         {
1411                             ERR("Missing EWX_SHUTDOWN flag for poweroff or reboot; action 0x%x\n", Action);
1412                             return STATUS_INVALID_PARAMETER;
1413                         }
1414 
1415                         /* Now we can locally remove it for performing checks */
1416                         Action &= ~EWX_SHUTDOWN;
1417                     }
1418 
1419                     /* Check parameters */
1420                     if (Action & EWX_FORCE)
1421                     {
1422                         // FIXME!
1423                         ERR("FIXME: EWX_FORCE present for Winlogon, what to do?\n");
1424                         Action &= ~EWX_FORCE;
1425                     }
1426                     switch (Action)
1427                     {
1428                         case EWX_LOGOFF:
1429                             wlxAction = WLX_SAS_ACTION_LOGOFF;
1430                             break;
1431                         case EWX_SHUTDOWN:
1432                             wlxAction = WLX_SAS_ACTION_SHUTDOWN;
1433                             break;
1434                         case EWX_REBOOT:
1435                             wlxAction = WLX_SAS_ACTION_SHUTDOWN_REBOOT;
1436                             break;
1437                         case EWX_POWEROFF:
1438                             wlxAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
1439                             break;
1440 
1441                         default:
1442                         {
1443                             ERR("Invalid ExitWindows action 0x%x\n", Action);
1444                             return STATUS_INVALID_PARAMETER;
1445                         }
1446                     }
1447 
1448                     TRACE("In LN_LOGOFF, exit_in_progress == %s\n",
1449                         ExitReactOSInProgress ? "true" : "false");
1450 
1451                     /*
1452                      * In case a parallel shutdown request is done (while we are
1453                      * being to shut down) and it was not done by Winlogon itself,
1454                      * then just stop here.
1455                      */
1456 #if 0
1457 // This code is commented at the moment (even if it's correct) because
1458 // our log-offs do not really work: the shell is restarted, no app is killed
1459 // etc... and as a result you just get explorer opening "My Documents". And
1460 // if you try now a shut down, it won't work because winlogon thinks it is
1461 // still in the middle of a shutdown.
1462 // Maybe we also need to reset ExitReactOSInProgress somewhere else??
1463                     if (ExitReactOSInProgress && (lParam & EWX_CALLER_WINLOGON) == 0)
1464                     {
1465                         break;
1466                     }
1467 #endif
1468                     /* Now do the shutdown action proper */
1469                     DoGenericAction(Session, wlxAction);
1470                     return 1;
1471                 }
1472                 case LN_LOGOFF_CANCELED:
1473                 {
1474                     ERR("Logoff canceled!!, before: exit_in_progress == %s, after will be false\n",
1475                         ExitReactOSInProgress ? "true" : "false");
1476 
1477                     ExitReactOSInProgress = FALSE;
1478                     return 1;
1479                 }
1480                 default:
1481                 {
1482                     ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam);
1483                 }
1484             }
1485             return 0;
1486         }
1487         case WM_TIMER:
1488         {
1489             if (wParam == 1)
1490             {
1491                 KillTimer(hwndDlg, 1);
1492                 StartUserShell(Session);
1493             }
1494             break;
1495         }
1496         case WLX_WM_SAS:
1497         {
1498             DispatchSAS(Session, (DWORD)wParam);
1499             return TRUE;
1500         }
1501     }
1502 
1503     return DefWindowProc(hwndDlg, uMsg, wParam, lParam);
1504 }
1505 
1506 BOOL
1507 InitializeSAS(
1508     IN OUT PWLSESSION Session)
1509 {
1510     WNDCLASSEXW swc;
1511     BOOL ret = FALSE;
1512 
1513     if (!SwitchDesktop(Session->WinlogonDesktop))
1514     {
1515         ERR("WL: Failed to switch to winlogon desktop\n");
1516         goto cleanup;
1517     }
1518 
1519     /* Register SAS window class */
1520     swc.cbSize = sizeof(WNDCLASSEXW);
1521     swc.style = CS_SAVEBITS;
1522     swc.lpfnWndProc = SASWindowProc;
1523     swc.cbClsExtra = 0;
1524     swc.cbWndExtra = 0;
1525     swc.hInstance = hAppInstance;
1526     swc.hIcon = NULL;
1527     swc.hCursor = NULL;
1528     swc.hbrBackground = NULL;
1529     swc.lpszMenuName = NULL;
1530     swc.lpszClassName = WINLOGON_SAS_CLASS;
1531     swc.hIconSm = NULL;
1532     if (RegisterClassExW(&swc) == 0)
1533     {
1534         ERR("WL: Failed to register SAS window class\n");
1535         goto cleanup;
1536     }
1537 
1538     /* Create invisible SAS window */
1539     Session->SASWindow = CreateWindowExW(
1540         0,
1541         WINLOGON_SAS_CLASS,
1542         WINLOGON_SAS_TITLE,
1543         WS_POPUP,
1544         0, 0, 0, 0, 0, 0,
1545         hAppInstance, Session);
1546     if (!Session->SASWindow)
1547     {
1548         ERR("WL: Failed to create SAS window\n");
1549         goto cleanup;
1550     }
1551 
1552     /* Register SAS window to receive SAS notifications */
1553     if (!SetLogonNotifyWindow(Session->SASWindow))
1554     {
1555         ERR("WL: Failed to register SAS window\n");
1556         goto cleanup;
1557     }
1558 
1559     if (!SetDefaultLanguage(NULL))
1560         return FALSE;
1561 
1562     ret = TRUE;
1563 
1564 cleanup:
1565     if (!ret)
1566         UninitializeSAS(Session);
1567     return ret;
1568 }
1569