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