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