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