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)
12 /* INCLUDES *****************************************************************/
16 #define WIN32_LEAN_AND_MEAN
20 #include <ndk/setypes.h>
21 #include <ndk/sefuncs.h>
23 /* GLOBALS ******************************************************************/
25 #define WINLOGON_SAS_CLASS L"SAS Window class"
26 #define WINLOGON_SAS_TITLE L"SAS window"
28 #define HK_CTRL_ALT_DEL 0
29 #define HK_CTRL_SHIFT_ESC 1
31 // #define EWX_FLAGS_MASK 0x00000014
32 // #define EWX_ACTION_MASK ~EWX_FLAGS_MASK
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
38 typedef struct tagLOGOFF_SHUTDOWN_DATA
42 } LOGOFF_SHUTDOWN_DATA
, *PLOGOFF_SHUTDOWN_DATA
;
44 static BOOL ExitReactOSInProgress
= FALSE
;
46 /* FUNCTIONS ****************************************************************/
50 IN OUT PWLSESSION Session
)
55 if (!Session
->Gina
.Functions
.WlxStartApplication
)
58 if (!CreateEnvironmentBlock(
66 ret
= Session
->Gina
.Functions
.WlxStartApplication(
67 Session
->Gina
.Context
,
72 DestroyEnvironmentBlock(lpEnvironment
);
78 IN OUT PWLSESSION Session
)
80 LPVOID lpEnvironment
= NULL
;
84 /* Create environment block for the user */
85 if (!CreateEnvironmentBlock(&lpEnvironment
, Session
->UserToken
, TRUE
))
87 WARN("WL: CreateEnvironmentBlock() failed\n");
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
);
96 ret
= Session
->Gina
.Functions
.WlxActivateUserShell(
97 Session
->Gina
.Context
,
102 DestroyEnvironmentBlock(lpEnvironment
);
116 DWORD dwType
, dwSize
;
118 UNICODE_STRING ValueString
;
125 BaseKey
= HKEY_CURRENT_USER
;
126 SubKey
= L
"Control Panel\\International";
127 ValueName
= L
"Locale";
131 BaseKey
= HKEY_LOCAL_MACHINE
;
132 SubKey
= L
"System\\CurrentControlSet\\Control\\Nls\\Language";
133 ValueName
= L
"Default";
142 if (rc
!= ERROR_SUCCESS
)
144 TRACE("RegOpenKeyEx() failed with error %lu\n", rc
);
147 rc
= RegQueryValueExW(
154 if (rc
!= ERROR_SUCCESS
)
156 TRACE("RegQueryValueEx() failed with error %lu\n", rc
);
159 else if (dwType
!= REG_SZ
)
161 TRACE("Wrong type for %S\\%S registry entry (got 0x%lx, expected 0x%x)\n",
162 SubKey
, ValueName
, dwType
, REG_SZ
);
166 Value
= HeapAlloc(GetProcessHeap(), 0, dwSize
);
169 TRACE("HeapAlloc() failed\n");
172 rc
= RegQueryValueExW(
179 if (rc
!= ERROR_SUCCESS
)
181 TRACE("RegQueryValueEx() failed with error %lu\n", rc
);
185 /* Convert Value to a Lcid */
186 ValueString
.Length
= ValueString
.MaximumLength
= (USHORT
)dwSize
;
187 ValueString
.Buffer
= Value
;
188 Status
= RtlUnicodeStringToInteger(&ValueString
, 16, (PULONG
)&Lcid
);
189 if (!NT_SUCCESS(Status
))
191 TRACE("RtlUnicodeStringToInteger() failed with status 0x%08lx\n", Status
);
195 TRACE("%s language is 0x%08lx\n",
196 UserProfile
? "User" : "System", Lcid
);
197 Status
= NtSetDefaultLocale(UserProfile
, Lcid
);
198 if (!NT_SUCCESS(Status
))
200 TRACE("NtSetDefaultLocale() failed with status 0x%08lx\n", Status
);
210 HeapFree(GetProcessHeap(), 0, Value
);
220 typedef BOOL (WINAPI
*PLAYSOUNDW
)(LPCWSTR
,HMODULE
,DWORD
);
221 typedef UINT (WINAPI
*WAVEOUTGETNUMDEVS
)(VOID
);
223 WAVEOUTGETNUMDEVS waveOutGetNumDevs
;
228 hLibrary
= LoadLibraryW(L
"winmm.dll");
231 waveOutGetNumDevs
= (WAVEOUTGETNUMDEVS
)GetProcAddress(hLibrary
, "waveOutGetNumDevs");
232 if (waveOutGetNumDevs
)
234 NumDevs
= waveOutGetNumDevs();
241 FreeLibrary(hLibrary
);
246 Play
= (PLAYSOUNDW
)GetProcAddress(hLibrary
, "PlaySoundW");
249 Ret
= Play(FileName
, NULL
, Flags
);
251 FreeLibrary(hLibrary
);
259 PlayLogonSoundThread(
260 IN LPVOID lpParameter
)
262 BYTE TokenUserBuffer
[256];
263 PTOKEN_USER pTokenUser
= (TOKEN_USER
*)TokenUserBuffer
;
266 WCHAR wszBuffer
[MAX_PATH
] = {0};
267 WCHAR wszDest
[MAX_PATH
];
268 DWORD dwSize
= sizeof(wszBuffer
), dwType
;
269 SERVICE_STATUS_PROCESS Info
;
270 UNICODE_STRING SidString
;
273 SC_HANDLE hSCManager
, hService
;
275 /* Get SID of current user */
276 Status
= NtQueryInformationToken((HANDLE
)lpParameter
,
279 sizeof(TokenUserBuffer
),
281 if (!NT_SUCCESS(Status
))
283 ERR("NtQueryInformationToken failed: %x!\n", Status
);
287 /* Convert SID to string */
288 RtlInitEmptyUnicodeString(&SidString
, wszBuffer
, sizeof(wszBuffer
));
289 Status
= RtlConvertSidToUnicodeString(&SidString
, pTokenUser
->User
.Sid
, FALSE
);
290 if (!NT_SUCCESS(Status
))
292 ERR("RtlConvertSidToUnicodeString failed: %x!\n", Status
);
296 /* Build path to logon sound registry key.
297 Note: We can't use HKCU here, because Winlogon is owned by SYSTEM user */
298 if (FAILED(StringCbCopyW(wszBuffer
+ SidString
.Length
/sizeof(WCHAR
),
299 sizeof(wszBuffer
) - SidString
.Length
,
300 L
"\\AppEvents\\Schemes\\Apps\\.Default\\WindowsLogon\\.Current")))
302 /* SID is too long. Should not happen. */
303 ERR("StringCbCopyW failed!\n");
307 /* Open registry key and query sound path */
308 if (RegOpenKeyExW(HKEY_USERS
, wszBuffer
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
310 ERR("RegOpenKeyExW(%ls) failed!\n", wszBuffer
);
314 if (RegQueryValueExW(hKey
, NULL
, NULL
, &dwType
,
315 (LPBYTE
)wszBuffer
, &dwSize
) != ERROR_SUCCESS
||
316 (dwType
!= REG_SZ
&& dwType
!= REG_EXPAND_SZ
))
318 ERR("RegQueryValueExW failed!\n");
327 /* No sound has been set */
328 ERR("No sound has been set\n");
332 /* Expand environment variables */
333 if (!ExpandEnvironmentStringsW(wszBuffer
, wszDest
, MAX_PATH
))
335 ERR("ExpandEnvironmentStringsW failed!\n");
339 /* Open service manager */
340 hSCManager
= OpenSCManager(NULL
, NULL
, SC_MANAGER_CONNECT
);
343 ERR("OpenSCManager failed (%x)\n", GetLastError());
347 /* Open wdmaud service */
348 hService
= OpenServiceW(hSCManager
, L
"wdmaud", GENERIC_READ
);
351 /* Sound is not installed */
352 TRACE("Failed to open wdmaud service (%x)\n", GetLastError());
353 CloseServiceHandle(hSCManager
);
357 /* Wait for wdmaud start */
360 if (!QueryServiceStatusEx(hService
, SC_STATUS_PROCESS_INFO
, (LPBYTE
)&Info
, sizeof(SERVICE_STATUS_PROCESS
), &dwSize
))
362 TRACE("QueryServiceStatusEx failed (%x)\n", GetLastError());
366 if (Info
.dwCurrentState
== SERVICE_RUNNING
)
371 } while (Index
++ < 20);
373 CloseServiceHandle(hService
);
374 CloseServiceHandle(hSCManager
);
376 /* If wdmaud is not running exit */
377 if (Info
.dwCurrentState
!= SERVICE_RUNNING
)
379 WARN("wdmaud has not started!\n");
383 /* Sound subsystem is running. Play logon sound. */
384 TRACE("Playing logon sound: %ls\n", wszDest
);
385 PlaySoundRoutine(wszDest
, TRUE
, SND_FILENAME
);
392 IN OUT PWLSESSION Session
)
396 hThread
= CreateThread(NULL
, 0, PlayLogonSoundThread
, (PVOID
)Session
->UserToken
, 0, NULL
);
398 CloseHandle(hThread
);
404 IN OUT PWLSESSION Session
)
406 PROFILEINFOW ProfileInfo
;
409 /* Loading personal settings */
410 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_LOADINGYOURPERSONALSETTINGS
);
411 ProfileInfo
.hProfile
= INVALID_HANDLE_VALUE
;
412 if (0 == (Session
->Options
& WLX_LOGON_OPT_NO_PROFILE
))
414 if (Session
->Profile
== NULL
415 || (Session
->Profile
->dwType
!= WLX_PROFILE_TYPE_V1_0
416 && Session
->Profile
->dwType
!= WLX_PROFILE_TYPE_V2_0
))
418 ERR("WL: Wrong profile\n");
422 /* Load the user profile */
423 ZeroMemory(&ProfileInfo
, sizeof(PROFILEINFOW
));
424 ProfileInfo
.dwSize
= sizeof(PROFILEINFOW
);
425 ProfileInfo
.dwFlags
= 0;
426 ProfileInfo
.lpUserName
= Session
->MprNotifyInfo
.pszUserName
;
427 ProfileInfo
.lpProfilePath
= Session
->Profile
->pszProfile
;
428 if (Session
->Profile
->dwType
>= WLX_PROFILE_TYPE_V2_0
)
430 ProfileInfo
.lpDefaultPath
= Session
->Profile
->pszNetworkDefaultUserProfile
;
431 ProfileInfo
.lpServerName
= Session
->Profile
->pszServerName
;
432 ProfileInfo
.lpPolicyPath
= Session
->Profile
->pszPolicy
;
435 if (!LoadUserProfileW(Session
->UserToken
, &ProfileInfo
))
437 ERR("WL: LoadUserProfileW() failed\n");
442 /* Create environment block for the user */
443 if (!CreateUserEnvironment(Session
))
445 WARN("WL: SetUserEnvironment() failed\n");
449 CallNotificationDlls(Session
, LogonHandler
);
451 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_APPLYINGYOURPERSONALSETTINGS
);
452 UpdatePerUserSystemParameters(0, TRUE
);
454 /* Set default language */
455 if (!SetDefaultLanguage(TRUE
))
457 WARN("WL: SetDefaultLanguage() failed\n");
461 if (!StartUserShell(Session
))
463 //WCHAR StatusMsg[256];
464 WARN("WL: WlxActivateUserShell() failed\n");
465 //LoadStringW(hAppInstance, IDS_FAILEDACTIVATEUSERSHELL, StatusMsg, sizeof(StatusMsg) / sizeof(StatusMsg[0]));
466 //MessageBoxW(0, StatusMsg, NULL, MB_ICONERROR);
470 CallNotificationDlls(Session
, StartShellHandler
);
472 if (!InitializeScreenSaver(Session
))
473 WARN("WL: Failed to initialize screen saver\n");
475 Session
->hProfileInfo
= ProfileInfo
.hProfile
;
477 /* Logon has successed. Play sound. */
478 PlayLogonSound(Session
);
483 if (Session
->Profile
)
485 HeapFree(GetProcessHeap(), 0, Session
->Profile
->pszProfile
);
486 HeapFree(GetProcessHeap(), 0, Session
->Profile
);
488 Session
->Profile
= NULL
;
489 if (!ret
&& ProfileInfo
.hProfile
!= INVALID_HANDLE_VALUE
)
491 UnloadUserProfile(Session
->UserToken
, ProfileInfo
.hProfile
);
493 RemoveStatusMessage(Session
);
496 CloseHandle(Session
->UserToken
);
497 Session
->UserToken
= NULL
;
502 SwitchDesktop(Session
->ApplicationDesktop
);
503 Session
->LogonState
= STATE_LOGGED_ON
;
513 LogoffShutdownThread(
517 PLOGOFF_SHUTDOWN_DATA LSData
= (PLOGOFF_SHUTDOWN_DATA
)Parameter
;
520 if (LSData
->Session
->UserToken
!= NULL
&&
521 !ImpersonateLoggedOnUser(LSData
->Session
->UserToken
))
523 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
527 // FIXME: To be really fixed: need to check what needs to be kept and what needs to be removed there.
529 // uFlags = EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) |
530 // ((LSData->Flags & EWX_ACTION_MASK) == EWX_LOGOFF ? EWX_CALLER_WINLOGON_LOGOFF : 0);
532 uFlags
= EWX_CALLER_WINLOGON
| (LSData
->Flags
& 0x0F);
534 ERR("In LogoffShutdownThread with uFlags == 0x%x; exit_in_progress == %s\n",
535 uFlags
, ExitReactOSInProgress
? "true" : "false");
537 ExitReactOSInProgress
= TRUE
;
539 /* Close processes of the interactive user */
540 if (!ExitWindowsEx(uFlags
, 0))
542 ERR("Unable to kill user apps, error %lu\n", GetLastError());
546 if (LSData
->Session
->UserToken
)
559 PLOGOFF_SHUTDOWN_DATA LSData
= (PLOGOFF_SHUTDOWN_DATA
)Parameter
;
561 ERR("In KillComProcesses\n");
563 if (LSData
->Session
->UserToken
!= NULL
&&
564 !ImpersonateLoggedOnUser(LSData
->Session
->UserToken
))
566 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
570 /* Attempt to kill remaining processes. No notifications needed. */
571 if (!ExitWindowsEx(EWX_CALLER_WINLOGON
| EWX_NONOTIFY
| EWX_FORCE
| EWX_LOGOFF
, 0))
573 ERR("Unable to kill COM apps, error %lu\n", GetLastError());
577 if (LSData
->Session
->UserToken
)
585 CreateLogoffSecurityAttributes(
586 OUT PSECURITY_ATTRIBUTES
* ppsa
)
588 /* The following code is not working yet and messy */
589 /* Still, it gives some ideas about data types and functions involved and */
590 /* required to set up a SECURITY_DESCRIPTOR for a SECURITY_ATTRIBUTES */
591 /* instance for a thread, to allow that thread to ImpersonateLoggedOnUser(). */
592 /* Specifically THREAD_SET_THREAD_TOKEN is required. */
593 PSECURITY_DESCRIPTOR SecurityDescriptor
= NULL
;
594 PSECURITY_ATTRIBUTES psa
= 0;
597 EXPLICIT_ACCESS Access
;
598 PSID pEveryoneSID
= NULL
;
599 static SID_IDENTIFIER_AUTHORITY WorldAuthority
= { SECURITY_WORLD_SID_AUTHORITY
};
603 // Let's first try to enumerate what kind of data we need for this to ever work:
604 // 1. The Winlogon SID, to be able to give it THREAD_SET_THREAD_TOKEN.
605 // 2. The users SID (the user trying to logoff, or rather shut down the system).
606 // 3. At least two EXPLICIT_ACCESS instances:
607 // 3.1 One for Winlogon itself, giving it the rights
608 // required to THREAD_SET_THREAD_TOKEN (as it's needed to successfully call
609 // ImpersonateLoggedOnUser).
610 // 3.2 One for the user, to allow *that* thread to perform its work.
611 // 4. An ACL to hold the these EXPLICIT_ACCESS ACE's.
612 // 5. A SECURITY_DESCRIPTOR to hold the ACL, and finally.
613 // 6. A SECURITY_ATTRIBUTES instance to pull all of this required stuff
614 // together, to hand it to CreateThread.
616 // However, it seems struct LOGOFF_SHUTDOWN_DATA doesn't contain
617 // these required SID's, why they'd have to be added.
618 // The Winlogon's own SID should probably only be created once,
619 // while the user's SID obviously must be created for each new user.
620 // Might as well store it when the user logs on?
622 if(!AllocateAndInitializeSid(&WorldAuthority
,
628 ERR("Failed to initialize security descriptor for logoff thread!\n");
629 return STATUS_UNSUCCESSFUL
;
632 /* set up the required security attributes to be able to shut down */
633 /* To save space and time, allocate a single block of memory holding */
634 /* both SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR */
635 pMem
= HeapAlloc(GetProcessHeap(),
637 sizeof(SECURITY_ATTRIBUTES
) +
638 SECURITY_DESCRIPTOR_MIN_LENGTH
+
642 ERR("Failed to allocate memory for logoff security descriptor!\n");
643 return STATUS_NO_MEMORY
;
646 /* Note that the security descriptor needs to be in _absolute_ format, */
647 /* meaning its members must be pointers to other structures, rather */
648 /* than the relative format using offsets */
649 psa
= (PSECURITY_ATTRIBUTES
)pMem
;
650 SecurityDescriptor
= (PSECURITY_DESCRIPTOR
)(pMem
+ sizeof(SECURITY_ATTRIBUTES
));
651 pACL
= (PACL
)(((PBYTE
)SecurityDescriptor
) + SECURITY_DESCRIPTOR_MIN_LENGTH
);
653 // Initialize an EXPLICIT_ACCESS structure for an ACE.
654 // The ACE will allow this thread to log off (and shut down the system, currently).
655 ZeroMemory(&Access
, sizeof(Access
));
656 Access
.grfAccessPermissions
= THREAD_SET_THREAD_TOKEN
;
657 Access
.grfAccessMode
= SET_ACCESS
; // GRANT_ACCESS?
658 Access
.grfInheritance
= NO_INHERITANCE
;
659 Access
.Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
660 Access
.Trustee
.TrusteeType
= TRUSTEE_IS_WELL_KNOWN_GROUP
;
661 Access
.Trustee
.ptstrName
= pEveryoneSID
;
663 if (SetEntriesInAcl(1, &Access
, NULL
, &pACL
) != ERROR_SUCCESS
)
665 ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n");
667 HeapFree(GetProcessHeap(), 0, pMem
);
668 return STATUS_UNSUCCESSFUL
;
671 if (!InitializeSecurityDescriptor(SecurityDescriptor
, SECURITY_DESCRIPTOR_REVISION
))
673 ERR("Failed to initialize security descriptor for logoff thread!\n");
674 HeapFree(GetProcessHeap(), 0, pMem
);
675 return STATUS_UNSUCCESSFUL
;
678 if (!SetSecurityDescriptorDacl(SecurityDescriptor
,
679 TRUE
, // bDaclPresent flag
681 FALSE
)) // not a default DACL
683 ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
684 HeapFree(GetProcessHeap(), 0, pMem
);
685 return STATUS_UNSUCCESSFUL
;
688 psa
->nLength
= sizeof(SECURITY_ATTRIBUTES
);
689 psa
->lpSecurityDescriptor
= SecurityDescriptor
;
690 psa
->bInheritHandle
= FALSE
;
694 return STATUS_SUCCESS
;
699 DestroyLogoffSecurityAttributes(
700 IN PSECURITY_ATTRIBUTES psa
)
704 HeapFree(GetProcessHeap(), 0, psa
);
712 IN OUT PWLSESSION Session
,
715 PLOGOFF_SHUTDOWN_DATA LSData
;
716 PSECURITY_ATTRIBUTES psa
;
721 /* Prepare data for logoff thread */
722 LSData
= HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA
));
725 ERR("Failed to allocate mem for thread data\n");
726 return STATUS_NO_MEMORY
;
728 LSData
->Flags
= Flags
;
729 LSData
->Session
= Session
;
731 Status
= CreateLogoffSecurityAttributes(&psa
);
732 if (!NT_SUCCESS(Status
))
734 ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status
);
735 HeapFree(GetProcessHeap(), 0, LSData
);
739 /* Run logoff thread */
740 hThread
= CreateThread(psa
, 0, LogoffShutdownThread
, (LPVOID
)LSData
, 0, NULL
);
743 ERR("Unable to create logoff thread, error %lu\n", GetLastError());
744 DestroyLogoffSecurityAttributes(psa
);
745 HeapFree(GetProcessHeap(), 0, LSData
);
746 return STATUS_UNSUCCESSFUL
;
748 WaitForSingleObject(hThread
, INFINITE
);
749 if (!GetExitCodeThread(hThread
, &exitCode
))
751 ERR("Unable to get exit code of logoff thread (error %lu)\n", GetLastError());
752 CloseHandle(hThread
);
753 DestroyLogoffSecurityAttributes(psa
);
754 HeapFree(GetProcessHeap(), 0, LSData
);
755 return STATUS_UNSUCCESSFUL
;
757 CloseHandle(hThread
);
760 ERR("Logoff thread returned failure\n");
761 DestroyLogoffSecurityAttributes(psa
);
762 HeapFree(GetProcessHeap(), 0, LSData
);
763 return STATUS_UNSUCCESSFUL
;
766 SwitchDesktop(Session
->WinlogonDesktop
);
768 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOGGINGOFF);
770 // FIXME: Closing network connections!
771 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_CLOSINGNETWORKCONNECTIONS);
773 /* Kill remaining COM apps. Only at logoff! */
774 hThread
= CreateThread(psa
, 0, KillComProcesses
, (LPVOID
)LSData
, 0, NULL
);
777 WaitForSingleObject(hThread
, INFINITE
);
778 CloseHandle(hThread
);
781 /* We're done with the SECURITY_DESCRIPTOR */
782 DestroyLogoffSecurityAttributes(psa
);
785 HeapFree(GetProcessHeap(), 0, LSData
);
787 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_SAVEYOURSETTINGS
);
789 UnloadUserProfile(Session
->UserToken
, Session
->hProfileInfo
);
791 CallNotificationDlls(Session
, LogoffHandler
);
793 CloseHandle(Session
->UserToken
);
794 UpdatePerUserSystemParameters(0, FALSE
);
795 Session
->LogonState
= STATE_LOGGED_OFF
;
796 Session
->UserToken
= NULL
;
798 return STATUS_SUCCESS
;
804 ShutdownComputerWindowProc(
810 UNREFERENCED_PARAMETER(lParam
);
816 switch (LOWORD(wParam
))
818 case IDC_BTNSHTDOWNCOMPUTER
:
819 EndDialog(hwndDlg
, IDC_BTNSHTDOWNCOMPUTER
);
826 RemoveMenu(GetSystemMenu(hwndDlg
, FALSE
), SC_CLOSE
, MF_BYCOMMAND
);
827 SetFocus(GetDlgItem(hwndDlg
, IDC_BTNSHTDOWNCOMPUTER
));
837 IN OUT PWLSESSION Session
)
839 if (Session
->SASWindow
)
841 DestroyWindow(Session
->SASWindow
);
842 Session
->SASWindow
= NULL
;
844 if (Session
->hEndOfScreenSaverThread
)
845 SetEvent(Session
->hEndOfScreenSaverThread
);
846 UnregisterClassW(WINLOGON_SAS_CLASS
, hAppInstance
);
851 IN OUT PWLSESSION Session
,
854 PLOGOFF_SHUTDOWN_DATA LSData
;
859 // SwitchDesktop(Session->WinlogonDesktop);
860 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_REACTOSISSHUTTINGDOWN
);
862 /* Prepare data for shutdown thread */
863 LSData
= HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA
));
866 ERR("Failed to allocate mem for thread data\n");
867 return STATUS_NO_MEMORY
;
869 if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
)
870 LSData
->Flags
= EWX_POWEROFF
;
871 else if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_REBOOT
)
872 LSData
->Flags
= EWX_REBOOT
;
874 LSData
->Flags
= EWX_SHUTDOWN
;
875 LSData
->Session
= Session
;
877 // FIXME: We may need to specify this flag to really force application kill
878 // (we are shutting down ReactOS, not just logging off so no hangs, etc...
879 // should be allowed).
880 // LSData->Flags |= EWX_FORCE;
882 /* Run shutdown thread */
883 hThread
= CreateThread(NULL
, 0, LogoffShutdownThread
, (LPVOID
)LSData
, 0, NULL
);
886 ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
887 HeapFree(GetProcessHeap(), 0, LSData
);
888 return STATUS_UNSUCCESSFUL
;
890 WaitForSingleObject(hThread
, INFINITE
);
891 HeapFree(GetProcessHeap(), 0, LSData
);
892 if (!GetExitCodeThread(hThread
, &exitCode
))
894 ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError());
895 CloseHandle(hThread
);
896 return STATUS_UNSUCCESSFUL
;
898 CloseHandle(hThread
);
901 ERR("Shutdown thread returned failure\n");
902 return STATUS_UNSUCCESSFUL
;
905 CallNotificationDlls(Session
, ShutdownHandler
);
907 /* Destroy SAS window */
908 UninitializeSAS(Session
);
910 /* Now we can shut down NT */
911 ERR("Shutting down NT...\n");
912 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, TRUE
, FALSE
, &Old
);
913 if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_REBOOT
)
915 NtShutdownSystem(ShutdownReboot
);
921 /* FIXME - only show this dialog if it's a shutdown and the computer doesn't support APM */
922 DialogBox(hAppInstance
, MAKEINTRESOURCE(IDD_SHUTDOWNCOMPUTER
),
923 GetDesktopWindow(), ShutdownComputerWindowProc
);
925 NtShutdownSystem(ShutdownNoReboot
);
927 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, Old
, FALSE
, &Old
);
928 return STATUS_SUCCESS
;
934 IN OUT PWLSESSION Session
,
939 case WLX_SAS_ACTION_LOGON
: /* 0x01 */
940 if (Session
->LogonState
== STATE_LOGGED_OFF_SAS
)
942 if (!HandleLogon(Session
))
944 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
945 CallNotificationDlls(Session
, LogonHandler
);
949 case WLX_SAS_ACTION_NONE
: /* 0x02 */
950 if (Session
->LogonState
== STATE_LOGGED_OFF_SAS
)
952 Session
->LogonState
= STATE_LOGGED_OFF
;
953 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
955 else if (Session
->LogonState
== STATE_LOGGED_ON_SAS
)
957 Session
->LogonState
= STATE_LOGGED_ON
;
959 else if (Session
->LogonState
== STATE_LOCKED_SAS
)
961 Session
->LogonState
= STATE_LOCKED
;
962 Session
->Gina
.Functions
.WlxDisplayLockedNotice(Session
->Gina
.Context
);
965 case WLX_SAS_ACTION_LOCK_WKSTA
: /* 0x03 */
966 if (Session
->Gina
.Functions
.WlxIsLockOk(Session
->Gina
.Context
))
968 SwitchDesktop(Session
->WinlogonDesktop
);
969 Session
->LogonState
= STATE_LOCKED
;
970 Session
->Gina
.Functions
.WlxDisplayLockedNotice(Session
->Gina
.Context
);
971 CallNotificationDlls(Session
, LockHandler
);
974 case WLX_SAS_ACTION_LOGOFF
: /* 0x04 */
975 case WLX_SAS_ACTION_SHUTDOWN
: /* 0x05 */
976 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
: /* 0x0a */
977 case WLX_SAS_ACTION_SHUTDOWN_REBOOT
: /* 0x0b */
978 if (Session
->LogonState
!= STATE_LOGGED_OFF
)
980 if (!Session
->Gina
.Functions
.WlxIsLogoffOk(Session
->Gina
.Context
))
982 if (!NT_SUCCESS(HandleLogoff(Session
, EWX_LOGOFF
)))
984 RemoveStatusMessage(Session
);
987 Session
->Gina
.Functions
.WlxLogoff(Session
->Gina
.Context
);
989 if (WLX_SHUTTINGDOWN(wlxAction
))
991 // FIXME: WlxShutdown should be done from inside HandleShutdown,
992 // after having displayed "ReactOS is shutting down" message.
993 Session
->Gina
.Functions
.WlxShutdown(Session
->Gina
.Context
, wlxAction
);
994 if (!NT_SUCCESS(HandleShutdown(Session
, wlxAction
)))
996 RemoveStatusMessage(Session
);
997 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1002 RemoveStatusMessage(Session
);
1003 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1006 case WLX_SAS_ACTION_TASKLIST
: /* 0x07 */
1007 SwitchDesktop(Session
->ApplicationDesktop
);
1008 Session
->LogonState
= STATE_LOGGED_ON
;
1009 StartTaskManager(Session
);
1011 case WLX_SAS_ACTION_UNLOCK_WKSTA
: /* 0x08 */
1012 SwitchDesktop(Session
->ApplicationDesktop
);
1013 Session
->LogonState
= STATE_LOGGED_ON
;
1014 CallNotificationDlls(Session
, UnlockHandler
);
1017 WARN("Unknown SAS action 0x%lx\n", wlxAction
);
1021 DWORD WINAPI
SetWindowStationUser(HWINSTA hWinSta
, LUID
* pluid
, PSID psid
, DWORD sidSize
);
1024 AddAceToWindowStation(
1029 BOOL
AllowWinstaAccess(PWLSESSION Session
)
1031 BOOL bSuccess
= FALSE
;
1034 PTOKEN_GROUPS ptg
= NULL
;
1036 TOKEN_STATISTICS Stats
;
1040 // Get required buffer size and allocate the TOKEN_GROUPS buffer.
1042 if (!GetTokenInformation(Session
->UserToken
,
1048 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
1051 ptg
= (PTOKEN_GROUPS
)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwLength
);
1056 // Get the token group information from the access token.
1057 if (!GetTokenInformation(Session
->UserToken
,
1066 // Loop through the groups to find the logon SID.
1068 for (dwIndex
= 0; dwIndex
< ptg
->GroupCount
; dwIndex
++)
1070 if ((ptg
->Groups
[dwIndex
].Attributes
& SE_GROUP_LOGON_ID
)
1071 == SE_GROUP_LOGON_ID
)
1073 psid
= ptg
->Groups
[dwIndex
].Sid
;
1078 dwLength
= GetLengthSid(psid
);
1080 if (!GetTokenInformation(Session
->UserToken
,
1083 sizeof(TOKEN_STATISTICS
),
1086 WARN("Couldn't get Authentication id from user token!\n");
1090 AddAceToWindowStation(Session
->InteractiveWindowStation
, psid
);
1092 ret
= SetWindowStationUser(Session
->InteractiveWindowStation
,
1093 &Stats
.AuthenticationId
,
1096 TRACE("SetWindowStationUser returned 0x%x\n", ret
);
1102 // Free the buffer for the token groups.
1104 HeapFree(GetProcessHeap(), 0, (LPVOID
)ptg
);
1112 IN OUT PWLSESSION Session
,
1115 DWORD wlxAction
= WLX_SAS_ACTION_NONE
;
1117 PSID LogonSid
= NULL
; /* FIXME */
1118 BOOL bSecure
= TRUE
;
1122 case WLX_SAS_TYPE_CTRL_ALT_DEL
:
1123 switch (Session
->LogonState
)
1126 Session
->LogonState
= STATE_LOGGED_OFF
;
1127 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1130 case STATE_LOGGED_OFF
:
1131 Session
->LogonState
= STATE_LOGGED_OFF_SAS
;
1133 hwnd
= GetTopDialogWindow();
1135 SendMessage(hwnd
, WLX_WM_SAS
, 0, 0);
1137 Session
->Options
= 0;
1139 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxLoggedOutSAS(
1140 Session
->Gina
.Context
,
1145 &Session
->UserToken
,
1146 &Session
->MprNotifyInfo
,
1147 (PVOID
*)&Session
->Profile
);
1149 AllowWinstaAccess(Session
);
1152 case STATE_LOGGED_OFF_SAS
:
1153 /* Ignore SAS if we are already in an SAS state */
1156 case STATE_LOGGED_ON
:
1157 Session
->LogonState
= STATE_LOGGED_ON_SAS
;
1158 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxLoggedOnSAS(Session
->Gina
.Context
, dwSasType
, NULL
);
1161 case STATE_LOGGED_ON_SAS
:
1162 /* Ignore SAS if we are already in an SAS state */
1166 Session
->LogonState
= STATE_LOCKED_SAS
;
1168 hwnd
= GetTopDialogWindow();
1170 SendMessage(hwnd
, WLX_WM_SAS
, 0, 0);
1172 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxWkstaLockedSAS(Session
->Gina
.Context
, dwSasType
);
1175 case STATE_LOCKED_SAS
:
1176 /* Ignore SAS if we are already in an SAS state */
1184 case WLX_SAS_TYPE_TIMEOUT
:
1187 case WLX_SAS_TYPE_SCRNSVR_TIMEOUT
:
1188 if (!Session
->Gina
.Functions
.WlxScreenSaverNotify(Session
->Gina
.Context
, &bSecure
))
1190 /* Skip start of screen saver */
1191 SetEvent(Session
->hEndOfScreenSaver
);
1195 StartScreenSaver(Session
);
1198 wlxAction
= WLX_SAS_ACTION_LOCK_WKSTA
;
1199 // DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
1204 case WLX_SAS_TYPE_SCRNSVR_ACTIVITY
:
1205 SetEvent(Session
->hUserActivity
);
1209 DoGenericAction(Session
, wlxAction
);
1215 IN PWLSESSION Session
,
1218 /* Register Ctrl+Alt+Del Hotkey */
1219 if (!RegisterHotKey(hwndSAS
, HK_CTRL_ALT_DEL
, MOD_CONTROL
| MOD_ALT
, VK_DELETE
))
1221 ERR("WL: Unable to register Ctrl+Alt+Del hotkey!\n");
1225 /* Register Ctrl+Shift+Esc (optional) */
1226 Session
->TaskManHotkey
= RegisterHotKey(hwndSAS
, HK_CTRL_SHIFT_ESC
, MOD_CONTROL
| MOD_SHIFT
, VK_ESCAPE
);
1227 if (!Session
->TaskManHotkey
)
1228 WARN("WL: Warning: Unable to register Ctrl+Alt+Esc hotkey!\n");
1235 IN PWLSESSION Session
,
1238 /* Unregister hotkeys */
1239 UnregisterHotKey(hwndSAS
, HK_CTRL_ALT_DEL
);
1241 if (Session
->TaskManHotkey
)
1242 UnregisterHotKey(hwndSAS
, HK_CTRL_SHIFT_ESC
);
1249 HandleMessageBeep(UINT uType
)
1259 EventName
= L
"SystemDefault";
1261 case MB_ICONASTERISK
:
1262 EventName
= L
"SystemAsterisk";
1264 case MB_ICONEXCLAMATION
:
1265 EventName
= L
"SystemExclamation";
1268 EventName
= L
"SystemHand";
1270 case MB_ICONQUESTION
:
1271 EventName
= L
"SystemQuestion";
1274 WARN("Unhandled type %d\n", uType
);
1275 EventName
= L
"SystemDefault";
1278 return PlaySoundRoutine(EventName
, FALSE
, SND_ALIAS
| SND_NOWAIT
| SND_NOSTOP
| SND_ASYNC
);
1290 PWLSESSION Session
= (PWLSESSION
)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1298 case MAKELONG(MOD_CONTROL
| MOD_ALT
, VK_DELETE
):
1300 TRACE("SAS: CONTROL+ALT+DELETE\n");
1301 if (!Session
->Gina
.UseCtrlAltDelete
)
1303 PostMessageW(Session
->SASWindow
, WLX_WM_SAS
, WLX_SAS_TYPE_CTRL_ALT_DEL
, 0);
1306 case MAKELONG(MOD_CONTROL
| MOD_SHIFT
, VK_ESCAPE
):
1308 TRACE("SAS: CONTROL+SHIFT+ESCAPE\n");
1309 DoGenericAction(Session
, WLX_SAS_ACTION_TASKLIST
);
1317 /* Get the session pointer from the create data */
1318 Session
= (PWLSESSION
)((LPCREATESTRUCT
)lParam
)->lpCreateParams
;
1320 /* Save the Session pointer */
1321 SetWindowLongPtrW(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)Session
);
1324 return RegisterHotKeys(Session
, hwndDlg
);
1328 if (!GetSetupType())
1329 UnregisterHotKeys(Session
, hwndDlg
);
1332 case WM_SETTINGCHANGE
:
1334 UINT uiAction
= (UINT
)wParam
;
1335 if (uiAction
== SPI_SETSCREENSAVETIMEOUT
1336 || uiAction
== SPI_SETSCREENSAVEACTIVE
)
1338 SetEvent(Session
->hScreenSaverParametersChanged
);
1342 case WM_LOGONNOTIFY
:
1346 case LN_MESSAGE_BEEP
:
1348 return HandleMessageBeep(lParam
);
1350 case LN_SHELL_EXITED
:
1352 /* lParam is the exit code */
1355 SetTimer(hwndDlg
, 1, 1000, NULL
);
1359 case LN_START_SCREENSAVE
:
1361 DispatchSAS(Session
, WLX_SAS_TYPE_SCRNSVR_TIMEOUT
);
1364 case LN_LOCK_WORKSTATION
:
1366 DoGenericAction(Session
, WLX_SAS_ACTION_LOCK_WKSTA
);
1371 UINT Flags
= (UINT
)lParam
;
1372 UINT Action
= Flags
& EWX_ACTION_MASK
;
1375 TRACE("\tFlags : 0x%lx\n", lParam
);
1378 * Our caller (USERSRV) should have added the shutdown flag
1379 * when setting also poweroff or reboot.
1381 if (Action
& (EWX_POWEROFF
| EWX_REBOOT
))
1383 if ((Action
& EWX_SHUTDOWN
) == 0)
1385 ERR("Missing EWX_SHUTDOWN flag for poweroff or reboot; action 0x%x\n", Action
);
1386 return STATUS_INVALID_PARAMETER
;
1389 /* Now we can locally remove it for performing checks */
1390 Action
&= ~EWX_SHUTDOWN
;
1393 /* Check parameters */
1394 if (Action
& EWX_FORCE
)
1397 ERR("FIXME: EWX_FORCE present for Winlogon, what to do?\n");
1398 Action
&= ~EWX_FORCE
;
1403 wlxAction
= WLX_SAS_ACTION_LOGOFF
;
1406 wlxAction
= WLX_SAS_ACTION_SHUTDOWN
;
1409 wlxAction
= WLX_SAS_ACTION_SHUTDOWN_REBOOT
;
1412 wlxAction
= WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
;
1417 ERR("Invalid ExitWindows action 0x%x\n", Action
);
1418 return STATUS_INVALID_PARAMETER
;
1422 ERR("In LN_LOGOFF, exit_in_progress == %s\n",
1423 ExitReactOSInProgress
? "true" : "false");
1426 * In case a parallel shutdown request is done (while we are
1427 * being to shut down) and it was not done by Winlogon itself,
1428 * then just stop here.
1431 // This code is commented at the moment (even if it's correct) because
1432 // our log-offs do not really work: the shell is restarted, no app is killed
1433 // etc... and as a result you just get explorer opening "My Documents". And
1434 // if you try now a shut down, it won't work because winlogon thinks it is
1435 // still in the middle of a shutdown.
1436 // Maybe we also need to reset ExitReactOSInProgress somewhere else??
1437 if (ExitReactOSInProgress
&& (lParam
& EWX_CALLER_WINLOGON
) == 0)
1442 /* Now do the shutdown action proper */
1443 DoGenericAction(Session
, wlxAction
);
1446 case LN_LOGOFF_CANCELED
:
1448 ERR("Logoff canceled!!, before: exit_in_progress == %s, after will be false\n",
1449 ExitReactOSInProgress
? "true" : "false");
1451 ExitReactOSInProgress
= FALSE
;
1456 ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam
);
1465 KillTimer(hwndDlg
, 1);
1466 StartUserShell(Session
);
1472 DispatchSAS(Session
, (DWORD
)wParam
);
1477 return DefWindowProc(hwndDlg
, uMsg
, wParam
, lParam
);
1482 IN OUT PWLSESSION Session
)
1487 if (!SwitchDesktop(Session
->WinlogonDesktop
))
1489 ERR("WL: Failed to switch to winlogon desktop\n");
1493 /* Register SAS window class */
1494 swc
.cbSize
= sizeof(WNDCLASSEXW
);
1495 swc
.style
= CS_SAVEBITS
;
1496 swc
.lpfnWndProc
= SASWindowProc
;
1499 swc
.hInstance
= hAppInstance
;
1502 swc
.hbrBackground
= NULL
;
1503 swc
.lpszMenuName
= NULL
;
1504 swc
.lpszClassName
= WINLOGON_SAS_CLASS
;
1506 if (RegisterClassExW(&swc
) == 0)
1508 ERR("WL: Failed to register SAS window class\n");
1512 /* Create invisible SAS window */
1513 Session
->SASWindow
= CreateWindowExW(
1519 hAppInstance
, Session
);
1520 if (!Session
->SASWindow
)
1522 ERR("WL: Failed to create SAS window\n");
1526 /* Register SAS window to receive SAS notifications */
1527 if (!SetLogonNotifyWindow(Session
->SASWindow
))
1529 ERR("WL: Failed to register SAS window\n");
1533 if (!SetDefaultLanguage(FALSE
))
1540 UninitializeSAS(Session
);