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