xref: /reactos/base/system/winlogon/sas.c (revision 22635479)
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
StartTaskManager(IN OUT PWLSESSION Session)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
StartUserShell(IN OUT PWLSESSION Session)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
SetDefaultLanguage(IN PWLSESSION Session)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
PlaySoundRoutine(IN LPCWSTR FileName,IN UINT bLogon,IN UINT Flags)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
PlayLogonSoundThread(IN LPVOID lpParameter)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
PlayLogonSound(IN OUT PWLSESSION Session)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
PlayLogoffSound(_In_ PWLSESSION Session)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
PlayEventSound(_In_ PWLSESSION Session,_In_ LPCWSTR EventName)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
RestoreAllConnections(PWLSESSION Session)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
HandleLogon(IN OUT PWLSESSION Session)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
LogoffShutdownThread(LPVOID Parameter)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
KillComProcesses(LPVOID Parameter)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
CreateLogoffSecurityAttributes(OUT PSECURITY_ATTRIBUTES * ppsa)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
DestroyLogoffSecurityAttributes(IN PSECURITY_ATTRIBUTES psa)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
HandleLogoff(IN OUT PWLSESSION Session,IN UINT Flags)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
ShutdownComputerWindowProc(IN HWND hwndDlg,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam)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
UninitializeSAS(IN OUT PWLSESSION Session)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
HandleShutdown(IN OUT PWLSESSION Session,IN DWORD wlxAction)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
DoGenericAction(IN OUT PWLSESSION Session,IN DWORD wlxAction)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
DispatchSAS(IN OUT PWLSESSION Session,IN DWORD dwSasType)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
RegisterHotKeys(IN PWLSESSION Session,IN HWND hwndSAS)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
UnregisterHotKeys(IN PWLSESSION Session,IN HWND hwndSAS)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
HandleMessageBeep(_In_ PWLSESSION Session,_In_ UINT uType)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
SASWindowProc(IN HWND hwndDlg,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam)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
InitializeSAS(IN OUT PWLSESSION Session)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