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