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