[WINLOGON] On logoff, also disconnect from any remote location
[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 /* Cancel all the user connections */
661 WNetClearConnections(0);
662
663 if (LSData->Session->UserToken)
664 RevertToSelf();
665
666 return ret;
667 }
668
669 static
670 DWORD
671 WINAPI
672 KillComProcesses(
673 LPVOID Parameter)
674 {
675 DWORD ret = 1;
676 PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA)Parameter;
677
678 TRACE("In KillComProcesses\n");
679
680 if (LSData->Session->UserToken != NULL &&
681 !ImpersonateLoggedOnUser(LSData->Session->UserToken))
682 {
683 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
684 return 0;
685 }
686
687 /* Attempt to kill remaining processes. No notifications needed. */
688 if (!ExitWindowsEx(EWX_CALLER_WINLOGON | EWX_NONOTIFY | EWX_FORCE | EWX_LOGOFF, 0))
689 {
690 ERR("Unable to kill COM apps, error %lu\n", GetLastError());
691 ret = 0;
692 }
693
694 if (LSData->Session->UserToken)
695 RevertToSelf();
696
697 return ret;
698 }
699
700 static
701 NTSTATUS
702 CreateLogoffSecurityAttributes(
703 OUT PSECURITY_ATTRIBUTES* ppsa)
704 {
705 /* The following code is not working yet and messy */
706 /* Still, it gives some ideas about data types and functions involved and */
707 /* required to set up a SECURITY_DESCRIPTOR for a SECURITY_ATTRIBUTES */
708 /* instance for a thread, to allow that thread to ImpersonateLoggedOnUser(). */
709 /* Specifically THREAD_SET_THREAD_TOKEN is required. */
710 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
711 PSECURITY_ATTRIBUTES psa = 0;
712 BYTE* pMem;
713 PACL pACL;
714 EXPLICIT_ACCESS Access;
715 PSID pEveryoneSID = NULL;
716 static SID_IDENTIFIER_AUTHORITY WorldAuthority = { SECURITY_WORLD_SID_AUTHORITY };
717
718 *ppsa = NULL;
719
720 // Let's first try to enumerate what kind of data we need for this to ever work:
721 // 1. The Winlogon SID, to be able to give it THREAD_SET_THREAD_TOKEN.
722 // 2. The users SID (the user trying to logoff, or rather shut down the system).
723 // 3. At least two EXPLICIT_ACCESS instances:
724 // 3.1 One for Winlogon itself, giving it the rights
725 // required to THREAD_SET_THREAD_TOKEN (as it's needed to successfully call
726 // ImpersonateLoggedOnUser).
727 // 3.2 One for the user, to allow *that* thread to perform its work.
728 // 4. An ACL to hold the these EXPLICIT_ACCESS ACE's.
729 // 5. A SECURITY_DESCRIPTOR to hold the ACL, and finally.
730 // 6. A SECURITY_ATTRIBUTES instance to pull all of this required stuff
731 // together, to hand it to CreateThread.
732 //
733 // However, it seems struct LOGOFF_SHUTDOWN_DATA doesn't contain
734 // these required SID's, why they'd have to be added.
735 // The Winlogon's own SID should probably only be created once,
736 // while the user's SID obviously must be created for each new user.
737 // Might as well store it when the user logs on?
738
739 if(!AllocateAndInitializeSid(&WorldAuthority,
740 1,
741 SECURITY_WORLD_RID,
742 0, 0, 0, 0, 0, 0, 0,
743 &pEveryoneSID))
744 {
745 ERR("Failed to initialize security descriptor for logoff thread!\n");
746 return STATUS_UNSUCCESSFUL;
747 }
748
749 /* set up the required security attributes to be able to shut down */
750 /* To save space and time, allocate a single block of memory holding */
751 /* both SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR */
752 pMem = HeapAlloc(GetProcessHeap(),
753 0,
754 sizeof(SECURITY_ATTRIBUTES) +
755 SECURITY_DESCRIPTOR_MIN_LENGTH +
756 sizeof(ACL));
757 if (!pMem)
758 {
759 ERR("Failed to allocate memory for logoff security descriptor!\n");
760 return STATUS_NO_MEMORY;
761 }
762
763 /* Note that the security descriptor needs to be in _absolute_ format, */
764 /* meaning its members must be pointers to other structures, rather */
765 /* than the relative format using offsets */
766 psa = (PSECURITY_ATTRIBUTES)pMem;
767 SecurityDescriptor = (PSECURITY_DESCRIPTOR)(pMem + sizeof(SECURITY_ATTRIBUTES));
768 pACL = (PACL)(((PBYTE)SecurityDescriptor) + SECURITY_DESCRIPTOR_MIN_LENGTH);
769
770 // Initialize an EXPLICIT_ACCESS structure for an ACE.
771 // The ACE will allow this thread to log off (and shut down the system, currently).
772 ZeroMemory(&Access, sizeof(Access));
773 Access.grfAccessPermissions = THREAD_SET_THREAD_TOKEN;
774 Access.grfAccessMode = SET_ACCESS; // GRANT_ACCESS?
775 Access.grfInheritance = NO_INHERITANCE;
776 Access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
777 Access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
778 Access.Trustee.ptstrName = pEveryoneSID;
779
780 if (SetEntriesInAcl(1, &Access, NULL, &pACL) != ERROR_SUCCESS)
781 {
782 ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n");
783
784 HeapFree(GetProcessHeap(), 0, pMem);
785 return STATUS_UNSUCCESSFUL;
786 }
787
788 if (!InitializeSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
789 {
790 ERR("Failed to initialize security descriptor for logoff thread!\n");
791 HeapFree(GetProcessHeap(), 0, pMem);
792 return STATUS_UNSUCCESSFUL;
793 }
794
795 if (!SetSecurityDescriptorDacl(SecurityDescriptor,
796 TRUE, // bDaclPresent flag
797 pACL,
798 FALSE)) // not a default DACL
799 {
800 ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
801 HeapFree(GetProcessHeap(), 0, pMem);
802 return STATUS_UNSUCCESSFUL;
803 }
804
805 psa->nLength = sizeof(SECURITY_ATTRIBUTES);
806 psa->lpSecurityDescriptor = SecurityDescriptor;
807 psa->bInheritHandle = FALSE;
808
809 *ppsa = psa;
810
811 return STATUS_SUCCESS;
812 }
813
814 static
815 VOID
816 DestroyLogoffSecurityAttributes(
817 IN PSECURITY_ATTRIBUTES psa)
818 {
819 if (psa)
820 {
821 HeapFree(GetProcessHeap(), 0, psa);
822 }
823 }
824
825
826 static
827 NTSTATUS
828 HandleLogoff(
829 IN OUT PWLSESSION Session,
830 IN UINT Flags)
831 {
832 PLOGOFF_SHUTDOWN_DATA LSData;
833 PSECURITY_ATTRIBUTES psa;
834 HANDLE hThread;
835 DWORD exitCode;
836 NTSTATUS Status;
837
838 /* Prepare data for logoff thread */
839 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
840 if (!LSData)
841 {
842 ERR("Failed to allocate mem for thread data\n");
843 return STATUS_NO_MEMORY;
844 }
845 LSData->Flags = Flags;
846 LSData->Session = Session;
847
848 Status = CreateLogoffSecurityAttributes(&psa);
849 if (!NT_SUCCESS(Status))
850 {
851 ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status);
852 HeapFree(GetProcessHeap(), 0, LSData);
853 return Status;
854 }
855
856 /* Run logoff thread */
857 hThread = CreateThread(psa, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
858 if (!hThread)
859 {
860 ERR("Unable to create logoff thread, error %lu\n", GetLastError());
861 DestroyLogoffSecurityAttributes(psa);
862 HeapFree(GetProcessHeap(), 0, LSData);
863 return STATUS_UNSUCCESSFUL;
864 }
865 WaitForSingleObject(hThread, INFINITE);
866 if (!GetExitCodeThread(hThread, &exitCode))
867 {
868 ERR("Unable to get exit code of logoff thread (error %lu)\n", GetLastError());
869 CloseHandle(hThread);
870 DestroyLogoffSecurityAttributes(psa);
871 HeapFree(GetProcessHeap(), 0, LSData);
872 return STATUS_UNSUCCESSFUL;
873 }
874 CloseHandle(hThread);
875 if (exitCode == 0)
876 {
877 ERR("Logoff thread returned failure\n");
878 DestroyLogoffSecurityAttributes(psa);
879 HeapFree(GetProcessHeap(), 0, LSData);
880 return STATUS_UNSUCCESSFUL;
881 }
882
883 SwitchDesktop(Session->WinlogonDesktop);
884
885 // TODO: Play logoff sound!
886
887 SetWindowStationUser(Session->InteractiveWindowStation,
888 &LuidNone, NULL, 0);
889
890 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOGGINGOFF);
891
892 // FIXME: Closing network connections!
893 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_CLOSINGNETWORKCONNECTIONS);
894
895 /* Kill remaining COM apps. Only at logoff! */
896 hThread = CreateThread(psa, 0, KillComProcesses, (LPVOID)LSData, 0, NULL);
897 if (hThread)
898 {
899 WaitForSingleObject(hThread, INFINITE);
900 CloseHandle(hThread);
901 }
902
903 /* We're done with the SECURITY_DESCRIPTOR */
904 DestroyLogoffSecurityAttributes(psa);
905 psa = NULL;
906
907 HeapFree(GetProcessHeap(), 0, LSData);
908
909 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_SAVEYOURSETTINGS);
910
911 UnloadUserProfile(Session->UserToken, Session->hProfileInfo);
912
913 CallNotificationDlls(Session, LogoffHandler);
914
915 CloseHandle(Session->UserToken);
916 UpdatePerUserSystemParameters(0, FALSE);
917 Session->LogonState = STATE_LOGGED_OFF;
918 Session->UserToken = NULL;
919
920 return STATUS_SUCCESS;
921 }
922
923 static
924 INT_PTR
925 CALLBACK
926 ShutdownComputerWindowProc(
927 IN HWND hwndDlg,
928 IN UINT uMsg,
929 IN WPARAM wParam,
930 IN LPARAM lParam)
931 {
932 UNREFERENCED_PARAMETER(lParam);
933
934 switch (uMsg)
935 {
936 case WM_COMMAND:
937 {
938 switch (LOWORD(wParam))
939 {
940 case IDC_BTNSHTDOWNCOMPUTER:
941 EndDialog(hwndDlg, IDC_BTNSHTDOWNCOMPUTER);
942 return TRUE;
943 }
944 break;
945 }
946 case WM_INITDIALOG:
947 {
948 RemoveMenu(GetSystemMenu(hwndDlg, FALSE), SC_CLOSE, MF_BYCOMMAND);
949 SetFocus(GetDlgItem(hwndDlg, IDC_BTNSHTDOWNCOMPUTER));
950 return TRUE;
951 }
952 }
953 return FALSE;
954 }
955
956 static
957 VOID
958 UninitializeSAS(
959 IN OUT PWLSESSION Session)
960 {
961 if (Session->SASWindow)
962 {
963 DestroyWindow(Session->SASWindow);
964 Session->SASWindow = NULL;
965 }
966 if (Session->hEndOfScreenSaverThread)
967 SetEvent(Session->hEndOfScreenSaverThread);
968 UnregisterClassW(WINLOGON_SAS_CLASS, hAppInstance);
969 }
970
971 NTSTATUS
972 HandleShutdown(
973 IN OUT PWLSESSION Session,
974 IN DWORD wlxAction)
975 {
976 PLOGOFF_SHUTDOWN_DATA LSData;
977 HANDLE hThread;
978 DWORD exitCode;
979 BOOLEAN Old;
980
981 // SwitchDesktop(Session->WinlogonDesktop);
982 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_REACTOSISSHUTTINGDOWN);
983
984 /* Prepare data for shutdown thread */
985 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
986 if (!LSData)
987 {
988 ERR("Failed to allocate mem for thread data\n");
989 return STATUS_NO_MEMORY;
990 }
991 if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF)
992 LSData->Flags = EWX_POWEROFF;
993 else if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
994 LSData->Flags = EWX_REBOOT;
995 else
996 LSData->Flags = EWX_SHUTDOWN;
997 LSData->Session = Session;
998
999 // FIXME: We may need to specify this flag to really force application kill
1000 // (we are shutting down ReactOS, not just logging off so no hangs, etc...
1001 // should be allowed).
1002 // LSData->Flags |= EWX_FORCE;
1003
1004 /* Run shutdown thread */
1005 hThread = CreateThread(NULL, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
1006 if (!hThread)
1007 {
1008 ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
1009 HeapFree(GetProcessHeap(), 0, LSData);
1010 return STATUS_UNSUCCESSFUL;
1011 }
1012 WaitForSingleObject(hThread, INFINITE);
1013 HeapFree(GetProcessHeap(), 0, LSData);
1014 if (!GetExitCodeThread(hThread, &exitCode))
1015 {
1016 ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError());
1017 CloseHandle(hThread);
1018 return STATUS_UNSUCCESSFUL;
1019 }
1020 CloseHandle(hThread);
1021 if (exitCode == 0)
1022 {
1023 ERR("Shutdown thread returned failure\n");
1024 return STATUS_UNSUCCESSFUL;
1025 }
1026
1027 CallNotificationDlls(Session, ShutdownHandler);
1028
1029 /* Destroy SAS window */
1030 UninitializeSAS(Session);
1031
1032 /* Now we can shut down NT */
1033 ERR("Shutting down NT...\n");
1034 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old);
1035 if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
1036 {
1037 NtShutdownSystem(ShutdownReboot);
1038 }
1039 else
1040 {
1041 if (FALSE)
1042 {
1043 /* FIXME - only show this dialog if it's a shutdown and the computer doesn't support APM */
1044 DialogBox(hAppInstance, MAKEINTRESOURCE(IDD_SHUTDOWNCOMPUTER),
1045 GetDesktopWindow(), ShutdownComputerWindowProc);
1046 }
1047 NtShutdownSystem(ShutdownNoReboot);
1048 }
1049 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, Old, FALSE, &Old);
1050 return STATUS_SUCCESS;
1051 }
1052
1053 static
1054 VOID
1055 DoGenericAction(
1056 IN OUT PWLSESSION Session,
1057 IN DWORD wlxAction)
1058 {
1059 switch (wlxAction)
1060 {
1061 case WLX_SAS_ACTION_LOGON: /* 0x01 */
1062 if (Session->LogonState == STATE_LOGGED_OFF_SAS)
1063 {
1064 if (!HandleLogon(Session))
1065 {
1066 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
1067 CallNotificationDlls(Session, LogonHandler);
1068 }
1069 }
1070 break;
1071 case WLX_SAS_ACTION_NONE: /* 0x02 */
1072 if (Session->LogonState == STATE_LOGGED_OFF_SAS)
1073 {
1074 Session->LogonState = STATE_LOGGED_OFF;
1075 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
1076 }
1077 else if (Session->LogonState == STATE_LOGGED_ON_SAS)
1078 {
1079 Session->LogonState = STATE_LOGGED_ON;
1080 }
1081 else if (Session->LogonState == STATE_LOCKED_SAS)
1082 {
1083 Session->LogonState = STATE_LOCKED;
1084 Session->Gina.Functions.WlxDisplayLockedNotice(Session->Gina.Context);
1085 }
1086 break;
1087 case WLX_SAS_ACTION_LOCK_WKSTA: /* 0x03 */
1088 if (Session->Gina.Functions.WlxIsLockOk(Session->Gina.Context))
1089 {
1090 SwitchDesktop(Session->WinlogonDesktop);
1091 Session->LogonState = STATE_LOCKED;
1092 Session->Gina.Functions.WlxDisplayLockedNotice(Session->Gina.Context);
1093 CallNotificationDlls(Session, LockHandler);
1094 }
1095 break;
1096 case WLX_SAS_ACTION_LOGOFF: /* 0x04 */
1097 case WLX_SAS_ACTION_SHUTDOWN: /* 0x05 */
1098 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF: /* 0x0a */
1099 case WLX_SAS_ACTION_SHUTDOWN_REBOOT: /* 0x0b */
1100 if (Session->LogonState != STATE_LOGGED_OFF)
1101 {
1102 if (!Session->Gina.Functions.WlxIsLogoffOk(Session->Gina.Context))
1103 break;
1104 if (!NT_SUCCESS(HandleLogoff(Session, EWX_LOGOFF)))
1105 {
1106 RemoveStatusMessage(Session);
1107 break;
1108 }
1109 Session->Gina.Functions.WlxLogoff(Session->Gina.Context);
1110 }
1111 if (WLX_SHUTTINGDOWN(wlxAction))
1112 {
1113 // FIXME: WlxShutdown should be done from inside HandleShutdown,
1114 // after having displayed "ReactOS is shutting down" message.
1115 Session->Gina.Functions.WlxShutdown(Session->Gina.Context, wlxAction);
1116 if (!NT_SUCCESS(HandleShutdown(Session, wlxAction)))
1117 {
1118 RemoveStatusMessage(Session);
1119 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
1120 }
1121 }
1122 else
1123 {
1124 RemoveStatusMessage(Session);
1125 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
1126 }
1127 break;
1128 case WLX_SAS_ACTION_TASKLIST: /* 0x07 */
1129 SwitchDesktop(Session->ApplicationDesktop);
1130 Session->LogonState = STATE_LOGGED_ON;
1131 StartTaskManager(Session);
1132 break;
1133 case WLX_SAS_ACTION_UNLOCK_WKSTA: /* 0x08 */
1134 SwitchDesktop(Session->ApplicationDesktop);
1135 Session->LogonState = STATE_LOGGED_ON;
1136 CallNotificationDlls(Session, UnlockHandler);
1137 break;
1138 default:
1139 WARN("Unknown SAS action 0x%lx\n", wlxAction);
1140 }
1141 }
1142
1143 static
1144 VOID
1145 DispatchSAS(
1146 IN OUT PWLSESSION Session,
1147 IN DWORD dwSasType)
1148 {
1149 DWORD wlxAction = WLX_SAS_ACTION_NONE;
1150 PSID LogonSid = NULL; /* FIXME */
1151 BOOL bSecure = TRUE;
1152
1153 switch (dwSasType)
1154 {
1155 case WLX_SAS_TYPE_CTRL_ALT_DEL:
1156 switch (Session->LogonState)
1157 {
1158 case STATE_INIT:
1159 Session->LogonState = STATE_LOGGED_OFF;
1160 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
1161 return;
1162
1163 case STATE_LOGGED_OFF:
1164 Session->LogonState = STATE_LOGGED_OFF_SAS;
1165
1166 CloseAllDialogWindows();
1167
1168 Session->Options = 0;
1169
1170 wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOutSAS(
1171 Session->Gina.Context,
1172 Session->SASAction,
1173 &Session->LogonId,
1174 LogonSid,
1175 &Session->Options,
1176 &Session->UserToken,
1177 &Session->MprNotifyInfo,
1178 (PVOID*)&Session->Profile);
1179 break;
1180
1181 case STATE_LOGGED_OFF_SAS:
1182 /* Ignore SAS if we are already in an SAS state */
1183 return;
1184
1185 case STATE_LOGGED_ON:
1186 Session->LogonState = STATE_LOGGED_ON_SAS;
1187 wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOnSAS(Session->Gina.Context, dwSasType, NULL);
1188 break;
1189
1190 case STATE_LOGGED_ON_SAS:
1191 /* Ignore SAS if we are already in an SAS state */
1192 return;
1193
1194 case STATE_LOCKED:
1195 Session->LogonState = STATE_LOCKED_SAS;
1196
1197 CloseAllDialogWindows();
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 if (Session->LogonState == STATE_LOGGED_ON)
1337 DoGenericAction(Session, WLX_SAS_ACTION_TASKLIST);
1338 return TRUE;
1339 }
1340 }
1341 break;
1342 }
1343 case WM_CREATE:
1344 {
1345 /* Get the session pointer from the create data */
1346 Session = (PWLSESSION)((LPCREATESTRUCT)lParam)->lpCreateParams;
1347
1348 /* Save the Session pointer */
1349 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)Session);
1350 if (GetSetupType())
1351 return TRUE;
1352 return RegisterHotKeys(Session, hwndDlg);
1353 }
1354 case WM_DESTROY:
1355 {
1356 if (!GetSetupType())
1357 UnregisterHotKeys(Session, hwndDlg);
1358 return TRUE;
1359 }
1360 case WM_SETTINGCHANGE:
1361 {
1362 UINT uiAction = (UINT)wParam;
1363 if (uiAction == SPI_SETSCREENSAVETIMEOUT
1364 || uiAction == SPI_SETSCREENSAVEACTIVE)
1365 {
1366 SetEvent(Session->hScreenSaverParametersChanged);
1367 }
1368 return TRUE;
1369 }
1370 case WM_LOGONNOTIFY:
1371 {
1372 switch(wParam)
1373 {
1374 case LN_MESSAGE_BEEP:
1375 {
1376 return HandleMessageBeep(lParam);
1377 }
1378 case LN_SHELL_EXITED:
1379 {
1380 /* lParam is the exit code */
1381 if (lParam != 1 &&
1382 Session->LogonState != STATE_LOGGED_OFF &&
1383 Session->LogonState != STATE_LOGGED_OFF_SAS)
1384 {
1385 SetTimer(hwndDlg, 1, 1000, NULL);
1386 }
1387 break;
1388 }
1389 case LN_START_SCREENSAVE:
1390 {
1391 DispatchSAS(Session, WLX_SAS_TYPE_SCRNSVR_TIMEOUT);
1392 break;
1393 }
1394 case LN_LOCK_WORKSTATION:
1395 {
1396 DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
1397 break;
1398 }
1399 case LN_LOGOFF:
1400 {
1401 UINT Flags = (UINT)lParam;
1402 UINT Action = Flags & EWX_ACTION_MASK;
1403 DWORD wlxAction;
1404
1405 TRACE("\tFlags : 0x%lx\n", lParam);
1406
1407 /*
1408 * Our caller (USERSRV) should have added the shutdown flag
1409 * when setting also poweroff or reboot.
1410 */
1411 if (Action & (EWX_POWEROFF | EWX_REBOOT))
1412 {
1413 if ((Action & EWX_SHUTDOWN) == 0)
1414 {
1415 ERR("Missing EWX_SHUTDOWN flag for poweroff or reboot; action 0x%x\n", Action);
1416 return STATUS_INVALID_PARAMETER;
1417 }
1418
1419 /* Now we can locally remove it for performing checks */
1420 Action &= ~EWX_SHUTDOWN;
1421 }
1422
1423 /* Check parameters */
1424 if (Action & EWX_FORCE)
1425 {
1426 // FIXME!
1427 ERR("FIXME: EWX_FORCE present for Winlogon, what to do?\n");
1428 Action &= ~EWX_FORCE;
1429 }
1430 switch (Action)
1431 {
1432 case EWX_LOGOFF:
1433 wlxAction = WLX_SAS_ACTION_LOGOFF;
1434 break;
1435 case EWX_SHUTDOWN:
1436 wlxAction = WLX_SAS_ACTION_SHUTDOWN;
1437 break;
1438 case EWX_REBOOT:
1439 wlxAction = WLX_SAS_ACTION_SHUTDOWN_REBOOT;
1440 break;
1441 case EWX_POWEROFF:
1442 wlxAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
1443 break;
1444
1445 default:
1446 {
1447 ERR("Invalid ExitWindows action 0x%x\n", Action);
1448 return STATUS_INVALID_PARAMETER;
1449 }
1450 }
1451
1452 TRACE("In LN_LOGOFF, exit_in_progress == %s\n",
1453 ExitReactOSInProgress ? "true" : "false");
1454
1455 /*
1456 * In case a parallel shutdown request is done (while we are
1457 * being to shut down) and it was not done by Winlogon itself,
1458 * then just stop here.
1459 */
1460 #if 0
1461 // This code is commented at the moment (even if it's correct) because
1462 // our log-offs do not really work: the shell is restarted, no app is killed
1463 // etc... and as a result you just get explorer opening "My Documents". And
1464 // if you try now a shut down, it won't work because winlogon thinks it is
1465 // still in the middle of a shutdown.
1466 // Maybe we also need to reset ExitReactOSInProgress somewhere else??
1467 if (ExitReactOSInProgress && (lParam & EWX_CALLER_WINLOGON) == 0)
1468 {
1469 break;
1470 }
1471 #endif
1472 /* Now do the shutdown action proper */
1473 DoGenericAction(Session, wlxAction);
1474 return 1;
1475 }
1476 case LN_LOGOFF_CANCELED:
1477 {
1478 ERR("Logoff canceled!!, before: exit_in_progress == %s, after will be false\n",
1479 ExitReactOSInProgress ? "true" : "false");
1480
1481 ExitReactOSInProgress = FALSE;
1482 return 1;
1483 }
1484 default:
1485 {
1486 ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam);
1487 }
1488 }
1489 return 0;
1490 }
1491 case WM_TIMER:
1492 {
1493 if (wParam == 1)
1494 {
1495 KillTimer(hwndDlg, 1);
1496 StartUserShell(Session);
1497 }
1498 break;
1499 }
1500 case WLX_WM_SAS:
1501 {
1502 DispatchSAS(Session, (DWORD)wParam);
1503 return TRUE;
1504 }
1505 }
1506
1507 return DefWindowProc(hwndDlg, uMsg, wParam, lParam);
1508 }
1509
1510 BOOL
1511 InitializeSAS(
1512 IN OUT PWLSESSION Session)
1513 {
1514 WNDCLASSEXW swc;
1515 BOOL ret = FALSE;
1516
1517 if (!SwitchDesktop(Session->WinlogonDesktop))
1518 {
1519 ERR("WL: Failed to switch to winlogon desktop\n");
1520 goto cleanup;
1521 }
1522
1523 /* Register SAS window class */
1524 swc.cbSize = sizeof(WNDCLASSEXW);
1525 swc.style = CS_SAVEBITS;
1526 swc.lpfnWndProc = SASWindowProc;
1527 swc.cbClsExtra = 0;
1528 swc.cbWndExtra = 0;
1529 swc.hInstance = hAppInstance;
1530 swc.hIcon = NULL;
1531 swc.hCursor = NULL;
1532 swc.hbrBackground = NULL;
1533 swc.lpszMenuName = NULL;
1534 swc.lpszClassName = WINLOGON_SAS_CLASS;
1535 swc.hIconSm = NULL;
1536 if (RegisterClassExW(&swc) == 0)
1537 {
1538 ERR("WL: Failed to register SAS window class\n");
1539 goto cleanup;
1540 }
1541
1542 /* Create invisible SAS window */
1543 Session->SASWindow = CreateWindowExW(
1544 0,
1545 WINLOGON_SAS_CLASS,
1546 WINLOGON_SAS_TITLE,
1547 WS_POPUP,
1548 0, 0, 0, 0, 0, 0,
1549 hAppInstance, Session);
1550 if (!Session->SASWindow)
1551 {
1552 ERR("WL: Failed to create SAS window\n");
1553 goto cleanup;
1554 }
1555
1556 /* Register SAS window to receive SAS notifications */
1557 if (!SetLogonNotifyWindow(Session->SASWindow))
1558 {
1559 ERR("WL: Failed to register SAS window\n");
1560 goto cleanup;
1561 }
1562
1563 if (!SetDefaultLanguage(NULL))
1564 return FALSE;
1565
1566 ret = TRUE;
1567
1568 cleanup:
1569 if (!ret)
1570 UninitializeSAS(Session);
1571 return ret;
1572 }