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 LUID LuidNone
= {0, 0};
48 /* FUNCTIONS ****************************************************************/
52 IN OUT PWLSESSION Session
)
57 if (!Session
->Gina
.Functions
.WlxStartApplication
)
60 if (!CreateEnvironmentBlock(
68 ret
= Session
->Gina
.Functions
.WlxStartApplication(
69 Session
->Gina
.Context
,
74 DestroyEnvironmentBlock(lpEnvironment
);
80 IN OUT PWLSESSION Session
)
82 LPVOID lpEnvironment
= NULL
;
86 /* Create environment block for the user */
87 if (!CreateEnvironmentBlock(&lpEnvironment
, Session
->UserToken
, TRUE
))
89 WARN("WL: CreateEnvironmentBlock() failed\n");
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
);
98 ret
= Session
->Gina
.Functions
.WlxActivateUserShell(
99 Session
->Gina
.Context
,
104 DestroyEnvironmentBlock(lpEnvironment
);
111 IN PWLSESSION Session
)
116 HKEY UserKey
, hKey
= NULL
;
117 LPCWSTR SubKey
, ValueName
;
118 DWORD dwType
, dwSize
;
120 UNICODE_STRING ValueString
;
124 UserProfile
= (Session
&& Session
->UserToken
);
126 if (UserProfile
&& !ImpersonateLoggedOnUser(Session
->UserToken
))
128 ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
130 // FIXME: ... or use the default language of the system??
131 // UserProfile = FALSE;
136 rc
= RegOpenCurrentUser(MAXIMUM_ALLOWED
, &UserKey
);
137 if (rc
!= ERROR_SUCCESS
)
139 TRACE("RegOpenCurrentUser() failed with error %lu\n", rc
);
143 SubKey
= L
"Control Panel\\International";
144 ValueName
= L
"Locale";
149 SubKey
= L
"System\\CurrentControlSet\\Control\\Nls\\Language";
150 ValueName
= L
"Default";
153 rc
= RegOpenKeyExW(UserKey
? UserKey
: HKEY_LOCAL_MACHINE
,
160 RegCloseKey(UserKey
);
162 if (rc
!= ERROR_SUCCESS
)
164 TRACE("RegOpenKeyEx() failed with error %lu\n", rc
);
168 rc
= RegQueryValueExW(hKey
,
174 if (rc
!= ERROR_SUCCESS
)
176 TRACE("RegQueryValueEx() failed with error %lu\n", rc
);
179 else if (dwType
!= REG_SZ
)
181 TRACE("Wrong type for %S\\%S registry entry (got 0x%lx, expected 0x%x)\n",
182 SubKey
, ValueName
, dwType
, REG_SZ
);
186 Value
= HeapAlloc(GetProcessHeap(), 0, dwSize
);
189 TRACE("HeapAlloc() failed\n");
192 rc
= RegQueryValueExW(hKey
,
198 if (rc
!= ERROR_SUCCESS
)
200 TRACE("RegQueryValueEx() failed with error %lu\n", rc
);
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
))
210 TRACE("RtlUnicodeStringToInteger() failed with status 0x%08lx\n", Status
);
214 TRACE("%s language is 0x%08lx\n",
215 UserProfile
? "User" : "System", Lcid
);
216 Status
= NtSetDefaultLocale(UserProfile
, Lcid
);
217 if (!NT_SUCCESS(Status
))
219 TRACE("NtSetDefaultLocale() failed with status 0x%08lx\n", Status
);
227 HeapFree(GetProcessHeap(), 0, Value
);
244 typedef BOOL (WINAPI
*PLAYSOUNDW
)(LPCWSTR
,HMODULE
,DWORD
);
245 typedef UINT (WINAPI
*WAVEOUTGETNUMDEVS
)(VOID
);
247 WAVEOUTGETNUMDEVS waveOutGetNumDevs
;
252 hLibrary
= LoadLibraryW(L
"winmm.dll");
255 waveOutGetNumDevs
= (WAVEOUTGETNUMDEVS
)GetProcAddress(hLibrary
, "waveOutGetNumDevs");
256 if (waveOutGetNumDevs
)
258 NumDevs
= waveOutGetNumDevs();
265 FreeLibrary(hLibrary
);
270 Play
= (PLAYSOUNDW
)GetProcAddress(hLibrary
, "PlaySoundW");
273 Ret
= Play(FileName
, NULL
, Flags
);
275 FreeLibrary(hLibrary
);
283 PlayLogonSoundThread(
284 IN LPVOID lpParameter
)
286 BYTE TokenUserBuffer
[256];
287 PTOKEN_USER pTokenUser
= (TOKEN_USER
*)TokenUserBuffer
;
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
;
297 SC_HANDLE hSCManager
, hService
;
300 // FIXME: Isn't it possible to *JUST* impersonate the current user
301 // *AND* open its HKCU??
304 /* Get SID of current user */
305 Status
= NtQueryInformationToken((HANDLE
)lpParameter
,
308 sizeof(TokenUserBuffer
),
310 if (!NT_SUCCESS(Status
))
312 ERR("NtQueryInformationToken failed: %x!\n", Status
);
316 /* Convert SID to string */
317 RtlInitEmptyUnicodeString(&SidString
, wszBuffer
, sizeof(wszBuffer
));
318 Status
= RtlConvertSidToUnicodeString(&SidString
, pTokenUser
->User
.Sid
, FALSE
);
319 if (!NT_SUCCESS(Status
))
321 ERR("RtlConvertSidToUnicodeString failed: %x!\n", Status
);
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")))
331 /* SID is too long. Should not happen. */
332 ERR("StringCbCopyW failed!\n");
336 /* Open registry key and query sound path */
337 if (RegOpenKeyExW(HKEY_USERS
, wszBuffer
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
339 ERR("RegOpenKeyExW(%ls) failed!\n", wszBuffer
);
343 if (RegQueryValueExW(hKey
, NULL
, NULL
, &dwType
,
344 (LPBYTE
)wszBuffer
, &dwSize
) != ERROR_SUCCESS
||
345 (dwType
!= REG_SZ
&& dwType
!= REG_EXPAND_SZ
))
347 ERR("RegQueryValueExW failed!\n");
356 /* No sound has been set */
357 ERR("No sound has been set\n");
361 /* Expand environment variables */
362 if (!ExpandEnvironmentStringsW(wszBuffer
, wszDest
, MAX_PATH
))
364 ERR("ExpandEnvironmentStringsW failed!\n");
368 /* Open the service manager */
369 hSCManager
= OpenSCManager(NULL
, NULL
, SC_MANAGER_CONNECT
);
372 ERR("OpenSCManager failed (%x)\n", GetLastError());
376 /* Open the wdmaud service */
377 hService
= OpenServiceW(hSCManager
, L
"wdmaud", GENERIC_READ
);
380 /* The service is not installed */
381 TRACE("Failed to open wdmaud service (%x)\n", GetLastError());
382 CloseServiceHandle(hSCManager
);
386 /* Wait for wdmaud to start */
389 if (!QueryServiceStatusEx(hService
, SC_STATUS_PROCESS_INFO
, (LPBYTE
)&Info
, sizeof(SERVICE_STATUS_PROCESS
), &dwSize
))
391 TRACE("QueryServiceStatusEx failed (%x)\n", GetLastError());
395 if (Info
.dwCurrentState
== SERVICE_RUNNING
)
400 } while (Index
++ < 20);
402 CloseServiceHandle(hService
);
403 CloseServiceHandle(hSCManager
);
405 /* If wdmaud is not running exit */
406 if (Info
.dwCurrentState
!= SERVICE_RUNNING
)
408 WARN("wdmaud has not started!\n");
412 /* Sound subsystem is running. Play logon sound. */
413 TRACE("Playing logon sound: %ls\n", wszDest
);
414 PlaySoundRoutine(wszDest
, TRUE
, SND_FILENAME
);
421 IN OUT PWLSESSION Session
)
425 hThread
= CreateThread(NULL
, 0, PlayLogonSoundThread
, (PVOID
)Session
->UserToken
, 0, NULL
);
427 CloseHandle(hThread
);
431 AllowWinstaAccess(PWLSESSION Session
)
433 BOOL bSuccess
= FALSE
;
436 PTOKEN_GROUPS ptg
= NULL
;
438 TOKEN_STATISTICS Stats
;
442 // Get required buffer size and allocate the TOKEN_GROUPS buffer.
444 if (!GetTokenInformation(Session
->UserToken
,
450 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
453 ptg
= (PTOKEN_GROUPS
)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwLength
);
458 // Get the token group information from the access token.
459 if (!GetTokenInformation(Session
->UserToken
,
468 // Loop through the groups to find the logon SID.
470 for (dwIndex
= 0; dwIndex
< ptg
->GroupCount
; dwIndex
++)
472 if ((ptg
->Groups
[dwIndex
].Attributes
& SE_GROUP_LOGON_ID
)
473 == SE_GROUP_LOGON_ID
)
475 psid
= ptg
->Groups
[dwIndex
].Sid
;
480 dwLength
= GetLengthSid(psid
);
482 if (!GetTokenInformation(Session
->UserToken
,
485 sizeof(TOKEN_STATISTICS
),
488 WARN("Couldn't get Authentication id from user token!\n");
492 AddAceToWindowStation(Session
->InteractiveWindowStation
, psid
);
494 ret
= SetWindowStationUser(Session
->InteractiveWindowStation
,
495 &Stats
.AuthenticationId
,
498 TRACE("SetWindowStationUser returned 0x%x\n", ret
);
504 // Free the buffer for the token groups.
506 HeapFree(GetProcessHeap(), 0, (LPVOID
)ptg
);
514 IN OUT PWLSESSION Session
)
516 PROFILEINFOW ProfileInfo
;
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
))
524 if (Session
->Profile
== NULL
525 || (Session
->Profile
->dwType
!= WLX_PROFILE_TYPE_V1_0
526 && Session
->Profile
->dwType
!= WLX_PROFILE_TYPE_V2_0
))
528 ERR("WL: Wrong profile\n");
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
)
540 ProfileInfo
.lpDefaultPath
= Session
->Profile
->pszNetworkDefaultUserProfile
;
541 ProfileInfo
.lpServerName
= Session
->Profile
->pszServerName
;
542 ProfileInfo
.lpPolicyPath
= Session
->Profile
->pszPolicy
;
545 if (!LoadUserProfileW(Session
->UserToken
, &ProfileInfo
))
547 ERR("WL: LoadUserProfileW() failed\n");
552 /* Create environment block for the user */
553 if (!CreateUserEnvironment(Session
))
555 WARN("WL: SetUserEnvironment() failed\n");
559 CallNotificationDlls(Session
, LogonHandler
);
561 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_APPLYINGYOURPERSONALSETTINGS
);
562 UpdatePerUserSystemParameters(0, TRUE
);
564 /* Set default user language */
565 if (!SetDefaultLanguage(Session
))
567 WARN("WL: SetDefaultLanguage() failed\n");
571 AllowWinstaAccess(Session
);
573 if (!StartUserShell(Session
))
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);
582 CallNotificationDlls(Session
, StartShellHandler
);
584 if (!InitializeScreenSaver(Session
))
585 WARN("WL: Failed to initialize screen saver\n");
587 Session
->hProfileInfo
= ProfileInfo
.hProfile
;
589 /* Logon has succeeded. Play sound. */
590 PlayLogonSound(Session
);
595 if (Session
->Profile
)
597 HeapFree(GetProcessHeap(), 0, Session
->Profile
->pszProfile
);
598 HeapFree(GetProcessHeap(), 0, Session
->Profile
);
600 Session
->Profile
= NULL
;
601 if (!ret
&& ProfileInfo
.hProfile
!= INVALID_HANDLE_VALUE
)
603 UnloadUserProfile(Session
->UserToken
, ProfileInfo
.hProfile
);
605 RemoveStatusMessage(Session
);
608 SetWindowStationUser(Session
->InteractiveWindowStation
,
610 CloseHandle(Session
->UserToken
);
611 Session
->UserToken
= NULL
;
616 SwitchDesktop(Session
->ApplicationDesktop
);
617 Session
->LogonState
= STATE_LOGGED_ON
;
627 LogoffShutdownThread(
631 PLOGOFF_SHUTDOWN_DATA LSData
= (PLOGOFF_SHUTDOWN_DATA
)Parameter
;
634 if (LSData
->Session
->UserToken
!= NULL
&&
635 !ImpersonateLoggedOnUser(LSData
->Session
->UserToken
))
637 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
641 // FIXME: To be really fixed: need to check what needs to be kept and what needs to be removed there.
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);
646 uFlags
= EWX_CALLER_WINLOGON
| (LSData
->Flags
& 0x0F);
648 TRACE("In LogoffShutdownThread with uFlags == 0x%x; exit_in_progress == %s\n",
649 uFlags
, ExitReactOSInProgress
? "true" : "false");
651 ExitReactOSInProgress
= TRUE
;
653 /* Close processes of the interactive user */
654 if (!ExitWindowsEx(uFlags
, 0))
656 ERR("Unable to kill user apps, error %lu\n", GetLastError());
660 if (LSData
->Session
->UserToken
)
673 PLOGOFF_SHUTDOWN_DATA LSData
= (PLOGOFF_SHUTDOWN_DATA
)Parameter
;
675 TRACE("In KillComProcesses\n");
677 if (LSData
->Session
->UserToken
!= NULL
&&
678 !ImpersonateLoggedOnUser(LSData
->Session
->UserToken
))
680 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
684 /* Attempt to kill remaining processes. No notifications needed. */
685 if (!ExitWindowsEx(EWX_CALLER_WINLOGON
| EWX_NONOTIFY
| EWX_FORCE
| EWX_LOGOFF
, 0))
687 ERR("Unable to kill COM apps, error %lu\n", GetLastError());
691 if (LSData
->Session
->UserToken
)
699 CreateLogoffSecurityAttributes(
700 OUT PSECURITY_ATTRIBUTES
* ppsa
)
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;
711 EXPLICIT_ACCESS Access
;
712 PSID pEveryoneSID
= NULL
;
713 static SID_IDENTIFIER_AUTHORITY WorldAuthority
= { SECURITY_WORLD_SID_AUTHORITY
};
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.
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?
736 if(!AllocateAndInitializeSid(&WorldAuthority
,
742 ERR("Failed to initialize security descriptor for logoff thread!\n");
743 return STATUS_UNSUCCESSFUL
;
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(),
751 sizeof(SECURITY_ATTRIBUTES
) +
752 SECURITY_DESCRIPTOR_MIN_LENGTH
+
756 ERR("Failed to allocate memory for logoff security descriptor!\n");
757 return STATUS_NO_MEMORY
;
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
);
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
;
777 if (SetEntriesInAcl(1, &Access
, NULL
, &pACL
) != ERROR_SUCCESS
)
779 ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n");
781 HeapFree(GetProcessHeap(), 0, pMem
);
782 return STATUS_UNSUCCESSFUL
;
785 if (!InitializeSecurityDescriptor(SecurityDescriptor
, SECURITY_DESCRIPTOR_REVISION
))
787 ERR("Failed to initialize security descriptor for logoff thread!\n");
788 HeapFree(GetProcessHeap(), 0, pMem
);
789 return STATUS_UNSUCCESSFUL
;
792 if (!SetSecurityDescriptorDacl(SecurityDescriptor
,
793 TRUE
, // bDaclPresent flag
795 FALSE
)) // not a default DACL
797 ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
798 HeapFree(GetProcessHeap(), 0, pMem
);
799 return STATUS_UNSUCCESSFUL
;
802 psa
->nLength
= sizeof(SECURITY_ATTRIBUTES
);
803 psa
->lpSecurityDescriptor
= SecurityDescriptor
;
804 psa
->bInheritHandle
= FALSE
;
808 return STATUS_SUCCESS
;
813 DestroyLogoffSecurityAttributes(
814 IN PSECURITY_ATTRIBUTES psa
)
818 HeapFree(GetProcessHeap(), 0, psa
);
826 IN OUT PWLSESSION Session
,
829 PLOGOFF_SHUTDOWN_DATA LSData
;
830 PSECURITY_ATTRIBUTES psa
;
835 /* Prepare data for logoff thread */
836 LSData
= HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA
));
839 ERR("Failed to allocate mem for thread data\n");
840 return STATUS_NO_MEMORY
;
842 LSData
->Flags
= Flags
;
843 LSData
->Session
= Session
;
845 Status
= CreateLogoffSecurityAttributes(&psa
);
846 if (!NT_SUCCESS(Status
))
848 ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status
);
849 HeapFree(GetProcessHeap(), 0, LSData
);
853 /* Run logoff thread */
854 hThread
= CreateThread(psa
, 0, LogoffShutdownThread
, (LPVOID
)LSData
, 0, NULL
);
857 ERR("Unable to create logoff thread, error %lu\n", GetLastError());
858 DestroyLogoffSecurityAttributes(psa
);
859 HeapFree(GetProcessHeap(), 0, LSData
);
860 return STATUS_UNSUCCESSFUL
;
862 WaitForSingleObject(hThread
, INFINITE
);
863 if (!GetExitCodeThread(hThread
, &exitCode
))
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
;
871 CloseHandle(hThread
);
874 ERR("Logoff thread returned failure\n");
875 DestroyLogoffSecurityAttributes(psa
);
876 HeapFree(GetProcessHeap(), 0, LSData
);
877 return STATUS_UNSUCCESSFUL
;
880 SwitchDesktop(Session
->WinlogonDesktop
);
882 // TODO: Play logoff sound!
884 SetWindowStationUser(Session
->InteractiveWindowStation
,
887 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOGGINGOFF);
889 // FIXME: Closing network connections!
890 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_CLOSINGNETWORKCONNECTIONS);
892 /* Kill remaining COM apps. Only at logoff! */
893 hThread
= CreateThread(psa
, 0, KillComProcesses
, (LPVOID
)LSData
, 0, NULL
);
896 WaitForSingleObject(hThread
, INFINITE
);
897 CloseHandle(hThread
);
900 /* We're done with the SECURITY_DESCRIPTOR */
901 DestroyLogoffSecurityAttributes(psa
);
904 HeapFree(GetProcessHeap(), 0, LSData
);
906 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_SAVEYOURSETTINGS
);
908 UnloadUserProfile(Session
->UserToken
, Session
->hProfileInfo
);
910 CallNotificationDlls(Session
, LogoffHandler
);
912 CloseHandle(Session
->UserToken
);
913 UpdatePerUserSystemParameters(0, FALSE
);
914 Session
->LogonState
= STATE_LOGGED_OFF
;
915 Session
->UserToken
= NULL
;
917 return STATUS_SUCCESS
;
923 ShutdownComputerWindowProc(
929 UNREFERENCED_PARAMETER(lParam
);
935 switch (LOWORD(wParam
))
937 case IDC_BTNSHTDOWNCOMPUTER
:
938 EndDialog(hwndDlg
, IDC_BTNSHTDOWNCOMPUTER
);
945 RemoveMenu(GetSystemMenu(hwndDlg
, FALSE
), SC_CLOSE
, MF_BYCOMMAND
);
946 SetFocus(GetDlgItem(hwndDlg
, IDC_BTNSHTDOWNCOMPUTER
));
956 IN OUT PWLSESSION Session
)
958 if (Session
->SASWindow
)
960 DestroyWindow(Session
->SASWindow
);
961 Session
->SASWindow
= NULL
;
963 if (Session
->hEndOfScreenSaverThread
)
964 SetEvent(Session
->hEndOfScreenSaverThread
);
965 UnregisterClassW(WINLOGON_SAS_CLASS
, hAppInstance
);
970 IN OUT PWLSESSION Session
,
973 PLOGOFF_SHUTDOWN_DATA LSData
;
978 // SwitchDesktop(Session->WinlogonDesktop);
979 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_REACTOSISSHUTTINGDOWN
);
981 /* Prepare data for shutdown thread */
982 LSData
= HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA
));
985 ERR("Failed to allocate mem for thread data\n");
986 return STATUS_NO_MEMORY
;
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
;
993 LSData
->Flags
= EWX_SHUTDOWN
;
994 LSData
->Session
= Session
;
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;
1001 /* Run shutdown thread */
1002 hThread
= CreateThread(NULL
, 0, LogoffShutdownThread
, (LPVOID
)LSData
, 0, NULL
);
1005 ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
1006 HeapFree(GetProcessHeap(), 0, LSData
);
1007 return STATUS_UNSUCCESSFUL
;
1009 WaitForSingleObject(hThread
, INFINITE
);
1010 HeapFree(GetProcessHeap(), 0, LSData
);
1011 if (!GetExitCodeThread(hThread
, &exitCode
))
1013 ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError());
1014 CloseHandle(hThread
);
1015 return STATUS_UNSUCCESSFUL
;
1017 CloseHandle(hThread
);
1020 ERR("Shutdown thread returned failure\n");
1021 return STATUS_UNSUCCESSFUL
;
1024 CallNotificationDlls(Session
, ShutdownHandler
);
1026 /* Destroy SAS window */
1027 UninitializeSAS(Session
);
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
)
1034 NtShutdownSystem(ShutdownReboot
);
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
);
1044 NtShutdownSystem(ShutdownNoReboot
);
1046 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, Old
, FALSE
, &Old
);
1047 return STATUS_SUCCESS
;
1053 IN OUT PWLSESSION Session
,
1058 case WLX_SAS_ACTION_LOGON
: /* 0x01 */
1059 if (Session
->LogonState
== STATE_LOGGED_OFF_SAS
)
1061 if (!HandleLogon(Session
))
1063 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1064 CallNotificationDlls(Session
, LogonHandler
);
1068 case WLX_SAS_ACTION_NONE
: /* 0x02 */
1069 if (Session
->LogonState
== STATE_LOGGED_OFF_SAS
)
1071 Session
->LogonState
= STATE_LOGGED_OFF
;
1072 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1074 else if (Session
->LogonState
== STATE_LOGGED_ON_SAS
)
1076 Session
->LogonState
= STATE_LOGGED_ON
;
1078 else if (Session
->LogonState
== STATE_LOCKED_SAS
)
1080 Session
->LogonState
= STATE_LOCKED
;
1081 Session
->Gina
.Functions
.WlxDisplayLockedNotice(Session
->Gina
.Context
);
1084 case WLX_SAS_ACTION_LOCK_WKSTA
: /* 0x03 */
1085 if (Session
->Gina
.Functions
.WlxIsLockOk(Session
->Gina
.Context
))
1087 SwitchDesktop(Session
->WinlogonDesktop
);
1088 Session
->LogonState
= STATE_LOCKED
;
1089 Session
->Gina
.Functions
.WlxDisplayLockedNotice(Session
->Gina
.Context
);
1090 CallNotificationDlls(Session
, LockHandler
);
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
)
1099 if (!Session
->Gina
.Functions
.WlxIsLogoffOk(Session
->Gina
.Context
))
1101 if (!NT_SUCCESS(HandleLogoff(Session
, EWX_LOGOFF
)))
1103 RemoveStatusMessage(Session
);
1106 Session
->Gina
.Functions
.WlxLogoff(Session
->Gina
.Context
);
1108 if (WLX_SHUTTINGDOWN(wlxAction
))
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
)))
1115 RemoveStatusMessage(Session
);
1116 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1121 RemoveStatusMessage(Session
);
1122 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1125 case WLX_SAS_ACTION_TASKLIST
: /* 0x07 */
1126 SwitchDesktop(Session
->ApplicationDesktop
);
1127 Session
->LogonState
= STATE_LOGGED_ON
;
1128 StartTaskManager(Session
);
1130 case WLX_SAS_ACTION_UNLOCK_WKSTA
: /* 0x08 */
1131 SwitchDesktop(Session
->ApplicationDesktop
);
1132 Session
->LogonState
= STATE_LOGGED_ON
;
1133 CallNotificationDlls(Session
, UnlockHandler
);
1136 WARN("Unknown SAS action 0x%lx\n", wlxAction
);
1143 IN OUT PWLSESSION Session
,
1146 DWORD wlxAction
= WLX_SAS_ACTION_NONE
;
1147 PSID LogonSid
= NULL
; /* FIXME */
1148 BOOL bSecure
= TRUE
;
1152 case WLX_SAS_TYPE_CTRL_ALT_DEL
:
1153 switch (Session
->LogonState
)
1156 Session
->LogonState
= STATE_LOGGED_OFF
;
1157 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1160 case STATE_LOGGED_OFF
:
1161 Session
->LogonState
= STATE_LOGGED_OFF_SAS
;
1163 CloseAllDialogWindows();
1165 Session
->Options
= 0;
1167 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxLoggedOutSAS(
1168 Session
->Gina
.Context
,
1173 &Session
->UserToken
,
1174 &Session
->MprNotifyInfo
,
1175 (PVOID
*)&Session
->Profile
);
1178 case STATE_LOGGED_OFF_SAS
:
1179 /* Ignore SAS if we are already in an SAS state */
1182 case STATE_LOGGED_ON
:
1183 Session
->LogonState
= STATE_LOGGED_ON_SAS
;
1184 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxLoggedOnSAS(Session
->Gina
.Context
, dwSasType
, NULL
);
1187 case STATE_LOGGED_ON_SAS
:
1188 /* Ignore SAS if we are already in an SAS state */
1192 Session
->LogonState
= STATE_LOCKED_SAS
;
1194 CloseAllDialogWindows();
1196 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxWkstaLockedSAS(Session
->Gina
.Context
, dwSasType
);
1199 case STATE_LOCKED_SAS
:
1200 /* Ignore SAS if we are already in an SAS state */
1208 case WLX_SAS_TYPE_TIMEOUT
:
1211 case WLX_SAS_TYPE_SCRNSVR_TIMEOUT
:
1212 if (!Session
->Gina
.Functions
.WlxScreenSaverNotify(Session
->Gina
.Context
, &bSecure
))
1214 /* Skip start of screen saver */
1215 SetEvent(Session
->hEndOfScreenSaver
);
1219 StartScreenSaver(Session
);
1222 wlxAction
= WLX_SAS_ACTION_LOCK_WKSTA
;
1223 // DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
1228 case WLX_SAS_TYPE_SCRNSVR_ACTIVITY
:
1229 SetEvent(Session
->hUserActivity
);
1233 DoGenericAction(Session
, wlxAction
);
1239 IN PWLSESSION Session
,
1242 /* Register Ctrl+Alt+Del Hotkey */
1243 if (!RegisterHotKey(hwndSAS
, HK_CTRL_ALT_DEL
, MOD_CONTROL
| MOD_ALT
, VK_DELETE
))
1245 ERR("WL: Unable to register Ctrl+Alt+Del hotkey!\n");
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");
1259 IN PWLSESSION Session
,
1262 /* Unregister hotkeys */
1263 UnregisterHotKey(hwndSAS
, HK_CTRL_ALT_DEL
);
1265 if (Session
->TaskManHotkey
)
1266 UnregisterHotKey(hwndSAS
, HK_CTRL_SHIFT_ESC
);
1273 HandleMessageBeep(UINT uType
)
1283 EventName
= L
"SystemDefault";
1285 case MB_ICONASTERISK
:
1286 EventName
= L
"SystemAsterisk";
1288 case MB_ICONEXCLAMATION
:
1289 EventName
= L
"SystemExclamation";
1292 EventName
= L
"SystemHand";
1294 case MB_ICONQUESTION
:
1295 EventName
= L
"SystemQuestion";
1298 WARN("Unhandled type %d\n", uType
);
1299 EventName
= L
"SystemDefault";
1302 return PlaySoundRoutine(EventName
, FALSE
, SND_ALIAS
| SND_NOWAIT
| SND_NOSTOP
| SND_ASYNC
);
1314 PWLSESSION Session
= (PWLSESSION
)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1322 case MAKELONG(MOD_CONTROL
| MOD_ALT
, VK_DELETE
):
1324 TRACE("SAS: CONTROL+ALT+DELETE\n");
1325 if (!Session
->Gina
.UseCtrlAltDelete
)
1327 PostMessageW(Session
->SASWindow
, WLX_WM_SAS
, WLX_SAS_TYPE_CTRL_ALT_DEL
, 0);
1330 case MAKELONG(MOD_CONTROL
| MOD_SHIFT
, VK_ESCAPE
):
1332 TRACE("SAS: CONTROL+SHIFT+ESCAPE\n");
1333 if (Session
->LogonState
== STATE_LOGGED_ON
)
1334 DoGenericAction(Session
, WLX_SAS_ACTION_TASKLIST
);
1342 /* Get the session pointer from the create data */
1343 Session
= (PWLSESSION
)((LPCREATESTRUCT
)lParam
)->lpCreateParams
;
1345 /* Save the Session pointer */
1346 SetWindowLongPtrW(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)Session
);
1349 return RegisterHotKeys(Session
, hwndDlg
);
1353 if (!GetSetupType())
1354 UnregisterHotKeys(Session
, hwndDlg
);
1357 case WM_SETTINGCHANGE
:
1359 UINT uiAction
= (UINT
)wParam
;
1360 if (uiAction
== SPI_SETSCREENSAVETIMEOUT
1361 || uiAction
== SPI_SETSCREENSAVEACTIVE
)
1363 SetEvent(Session
->hScreenSaverParametersChanged
);
1367 case WM_LOGONNOTIFY
:
1371 case LN_MESSAGE_BEEP
:
1373 return HandleMessageBeep(lParam
);
1375 case LN_SHELL_EXITED
:
1377 /* lParam is the exit code */
1379 Session
->LogonState
!= STATE_LOGGED_OFF
&&
1380 Session
->LogonState
!= STATE_LOGGED_OFF_SAS
)
1382 SetTimer(hwndDlg
, 1, 1000, NULL
);
1386 case LN_START_SCREENSAVE
:
1388 DispatchSAS(Session
, WLX_SAS_TYPE_SCRNSVR_TIMEOUT
);
1391 case LN_LOCK_WORKSTATION
:
1393 DoGenericAction(Session
, WLX_SAS_ACTION_LOCK_WKSTA
);
1398 UINT Flags
= (UINT
)lParam
;
1399 UINT Action
= Flags
& EWX_ACTION_MASK
;
1402 TRACE("\tFlags : 0x%lx\n", lParam
);
1405 * Our caller (USERSRV) should have added the shutdown flag
1406 * when setting also poweroff or reboot.
1408 if (Action
& (EWX_POWEROFF
| EWX_REBOOT
))
1410 if ((Action
& EWX_SHUTDOWN
) == 0)
1412 ERR("Missing EWX_SHUTDOWN flag for poweroff or reboot; action 0x%x\n", Action
);
1413 return STATUS_INVALID_PARAMETER
;
1416 /* Now we can locally remove it for performing checks */
1417 Action
&= ~EWX_SHUTDOWN
;
1420 /* Check parameters */
1421 if (Action
& EWX_FORCE
)
1424 ERR("FIXME: EWX_FORCE present for Winlogon, what to do?\n");
1425 Action
&= ~EWX_FORCE
;
1430 wlxAction
= WLX_SAS_ACTION_LOGOFF
;
1433 wlxAction
= WLX_SAS_ACTION_SHUTDOWN
;
1436 wlxAction
= WLX_SAS_ACTION_SHUTDOWN_REBOOT
;
1439 wlxAction
= WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
;
1444 ERR("Invalid ExitWindows action 0x%x\n", Action
);
1445 return STATUS_INVALID_PARAMETER
;
1449 TRACE("In LN_LOGOFF, exit_in_progress == %s\n",
1450 ExitReactOSInProgress
? "true" : "false");
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.
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)
1469 /* Now do the shutdown action proper */
1470 DoGenericAction(Session
, wlxAction
);
1473 case LN_LOGOFF_CANCELED
:
1475 ERR("Logoff canceled!!, before: exit_in_progress == %s, after will be false\n",
1476 ExitReactOSInProgress
? "true" : "false");
1478 ExitReactOSInProgress
= FALSE
;
1483 ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam
);
1492 KillTimer(hwndDlg
, 1);
1493 StartUserShell(Session
);
1499 DispatchSAS(Session
, (DWORD
)wParam
);
1504 return DefWindowProc(hwndDlg
, uMsg
, wParam
, lParam
);
1509 IN OUT PWLSESSION Session
)
1514 if (!SwitchDesktop(Session
->WinlogonDesktop
))
1516 ERR("WL: Failed to switch to winlogon desktop\n");
1520 /* Register SAS window class */
1521 swc
.cbSize
= sizeof(WNDCLASSEXW
);
1522 swc
.style
= CS_SAVEBITS
;
1523 swc
.lpfnWndProc
= SASWindowProc
;
1526 swc
.hInstance
= hAppInstance
;
1529 swc
.hbrBackground
= NULL
;
1530 swc
.lpszMenuName
= NULL
;
1531 swc
.lpszClassName
= WINLOGON_SAS_CLASS
;
1533 if (RegisterClassExW(&swc
) == 0)
1535 ERR("WL: Failed to register SAS window class\n");
1539 /* Create invisible SAS window */
1540 Session
->SASWindow
= CreateWindowExW(
1546 hAppInstance
, Session
);
1547 if (!Session
->SASWindow
)
1549 ERR("WL: Failed to create SAS window\n");
1553 /* Register SAS window to receive SAS notifications */
1554 if (!SetLogonNotifyWindow(Session
->SASWindow
))
1556 ERR("WL: Failed to register SAS window\n");
1560 if (!SetDefaultLanguage(NULL
))
1567 UninitializeSAS(Session
);