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