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