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