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