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
);
109 IN PWLSESSION Session
)
114 HKEY UserKey
, hKey
= NULL
;
115 LPCWSTR SubKey
, ValueName
;
116 DWORD dwType
, dwSize
;
118 UNICODE_STRING ValueString
;
122 UserProfile
= (Session
&& Session
->UserToken
);
124 if (UserProfile
&& !ImpersonateLoggedOnUser(Session
->UserToken
))
126 ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
128 // FIXME: ... or use the default language of the system??
129 // UserProfile = FALSE;
134 rc
= RegOpenCurrentUser(MAXIMUM_ALLOWED
, &UserKey
);
135 if (rc
!= ERROR_SUCCESS
)
137 TRACE("RegOpenCurrentUser() failed with error %lu\n", rc
);
141 SubKey
= L
"Control Panel\\International";
142 ValueName
= L
"Locale";
147 SubKey
= L
"System\\CurrentControlSet\\Control\\Nls\\Language";
148 ValueName
= L
"Default";
151 rc
= RegOpenKeyExW(UserKey
? UserKey
: HKEY_LOCAL_MACHINE
,
158 RegCloseKey(UserKey
);
160 if (rc
!= ERROR_SUCCESS
)
162 TRACE("RegOpenKeyEx() failed with error %lu\n", rc
);
166 rc
= RegQueryValueExW(hKey
,
172 if (rc
!= ERROR_SUCCESS
)
174 TRACE("RegQueryValueEx() failed with error %lu\n", rc
);
177 else if (dwType
!= REG_SZ
)
179 TRACE("Wrong type for %S\\%S registry entry (got 0x%lx, expected 0x%x)\n",
180 SubKey
, ValueName
, dwType
, REG_SZ
);
184 Value
= HeapAlloc(GetProcessHeap(), 0, dwSize
);
187 TRACE("HeapAlloc() failed\n");
190 rc
= RegQueryValueExW(hKey
,
196 if (rc
!= ERROR_SUCCESS
)
198 TRACE("RegQueryValueEx() failed with error %lu\n", rc
);
202 /* Convert Value to a Lcid */
203 ValueString
.Length
= ValueString
.MaximumLength
= (USHORT
)dwSize
;
204 ValueString
.Buffer
= Value
;
205 Status
= RtlUnicodeStringToInteger(&ValueString
, 16, (PULONG
)&Lcid
);
206 if (!NT_SUCCESS(Status
))
208 TRACE("RtlUnicodeStringToInteger() failed with status 0x%08lx\n", Status
);
212 TRACE("%s language is 0x%08lx\n",
213 UserProfile
? "User" : "System", Lcid
);
214 Status
= NtSetDefaultLocale(UserProfile
, Lcid
);
215 if (!NT_SUCCESS(Status
))
217 TRACE("NtSetDefaultLocale() failed with status 0x%08lx\n", Status
);
225 HeapFree(GetProcessHeap(), 0, Value
);
242 typedef BOOL (WINAPI
*PLAYSOUNDW
)(LPCWSTR
,HMODULE
,DWORD
);
243 typedef UINT (WINAPI
*WAVEOUTGETNUMDEVS
)(VOID
);
245 WAVEOUTGETNUMDEVS waveOutGetNumDevs
;
250 hLibrary
= LoadLibraryW(L
"winmm.dll");
253 waveOutGetNumDevs
= (WAVEOUTGETNUMDEVS
)GetProcAddress(hLibrary
, "waveOutGetNumDevs");
254 if (waveOutGetNumDevs
)
256 NumDevs
= waveOutGetNumDevs();
263 FreeLibrary(hLibrary
);
268 Play
= (PLAYSOUNDW
)GetProcAddress(hLibrary
, "PlaySoundW");
271 Ret
= Play(FileName
, NULL
, Flags
);
273 FreeLibrary(hLibrary
);
281 PlayLogonSoundThread(
282 IN LPVOID lpParameter
)
284 BYTE TokenUserBuffer
[256];
285 PTOKEN_USER pTokenUser
= (TOKEN_USER
*)TokenUserBuffer
;
288 WCHAR wszBuffer
[MAX_PATH
] = {0};
289 WCHAR wszDest
[MAX_PATH
];
290 DWORD dwSize
= sizeof(wszBuffer
), dwType
;
291 SERVICE_STATUS_PROCESS Info
;
292 UNICODE_STRING SidString
;
295 SC_HANDLE hSCManager
, hService
;
298 // FIXME: Isn't it possible to *JUST* impersonate the current user
299 // *AND* open its HKCU??
302 /* Get SID of current user */
303 Status
= NtQueryInformationToken((HANDLE
)lpParameter
,
306 sizeof(TokenUserBuffer
),
308 if (!NT_SUCCESS(Status
))
310 ERR("NtQueryInformationToken failed: %x!\n", Status
);
314 /* Convert SID to string */
315 RtlInitEmptyUnicodeString(&SidString
, wszBuffer
, sizeof(wszBuffer
));
316 Status
= RtlConvertSidToUnicodeString(&SidString
, pTokenUser
->User
.Sid
, FALSE
);
317 if (!NT_SUCCESS(Status
))
319 ERR("RtlConvertSidToUnicodeString failed: %x!\n", Status
);
323 /* Build path to logon sound registry key.
324 Note: We can't use HKCU here, because Winlogon is owned by SYSTEM user */
325 if (FAILED(StringCbCopyW(wszBuffer
+ SidString
.Length
/sizeof(WCHAR
),
326 sizeof(wszBuffer
) - SidString
.Length
,
327 L
"\\AppEvents\\Schemes\\Apps\\.Default\\WindowsLogon\\.Current")))
329 /* SID is too long. Should not happen. */
330 ERR("StringCbCopyW failed!\n");
334 /* Open registry key and query sound path */
335 if (RegOpenKeyExW(HKEY_USERS
, wszBuffer
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
337 ERR("RegOpenKeyExW(%ls) failed!\n", wszBuffer
);
341 if (RegQueryValueExW(hKey
, NULL
, NULL
, &dwType
,
342 (LPBYTE
)wszBuffer
, &dwSize
) != ERROR_SUCCESS
||
343 (dwType
!= REG_SZ
&& dwType
!= REG_EXPAND_SZ
))
345 ERR("RegQueryValueExW failed!\n");
354 /* No sound has been set */
355 ERR("No sound has been set\n");
359 /* Expand environment variables */
360 if (!ExpandEnvironmentStringsW(wszBuffer
, wszDest
, MAX_PATH
))
362 ERR("ExpandEnvironmentStringsW failed!\n");
366 /* Open the service manager */
367 hSCManager
= OpenSCManager(NULL
, NULL
, SC_MANAGER_CONNECT
);
370 ERR("OpenSCManager failed (%x)\n", GetLastError());
374 /* Open the wdmaud service */
375 hService
= OpenServiceW(hSCManager
, L
"wdmaud", GENERIC_READ
);
378 /* The service is not installed */
379 TRACE("Failed to open wdmaud service (%x)\n", GetLastError());
380 CloseServiceHandle(hSCManager
);
384 /* Wait for wdmaud to start */
387 if (!QueryServiceStatusEx(hService
, SC_STATUS_PROCESS_INFO
, (LPBYTE
)&Info
, sizeof(SERVICE_STATUS_PROCESS
), &dwSize
))
389 TRACE("QueryServiceStatusEx failed (%x)\n", GetLastError());
393 if (Info
.dwCurrentState
== SERVICE_RUNNING
)
398 } while (Index
++ < 20);
400 CloseServiceHandle(hService
);
401 CloseServiceHandle(hSCManager
);
403 /* If wdmaud is not running exit */
404 if (Info
.dwCurrentState
!= SERVICE_RUNNING
)
406 WARN("wdmaud has not started!\n");
410 /* Sound subsystem is running. Play logon sound. */
411 TRACE("Playing logon sound: %ls\n", wszDest
);
412 PlaySoundRoutine(wszDest
, TRUE
, SND_FILENAME
);
419 IN OUT PWLSESSION Session
)
423 hThread
= CreateThread(NULL
, 0, PlayLogonSoundThread
, (PVOID
)Session
->UserToken
, 0, NULL
);
425 CloseHandle(hThread
);
431 IN OUT PWLSESSION Session
)
433 PROFILEINFOW ProfileInfo
;
436 /* Loading personal settings */
437 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_LOADINGYOURPERSONALSETTINGS
);
438 ProfileInfo
.hProfile
= INVALID_HANDLE_VALUE
;
439 if (0 == (Session
->Options
& WLX_LOGON_OPT_NO_PROFILE
))
441 if (Session
->Profile
== NULL
442 || (Session
->Profile
->dwType
!= WLX_PROFILE_TYPE_V1_0
443 && Session
->Profile
->dwType
!= WLX_PROFILE_TYPE_V2_0
))
445 ERR("WL: Wrong profile\n");
449 /* Load the user profile */
450 ZeroMemory(&ProfileInfo
, sizeof(PROFILEINFOW
));
451 ProfileInfo
.dwSize
= sizeof(PROFILEINFOW
);
452 ProfileInfo
.dwFlags
= 0;
453 ProfileInfo
.lpUserName
= Session
->MprNotifyInfo
.pszUserName
;
454 ProfileInfo
.lpProfilePath
= Session
->Profile
->pszProfile
;
455 if (Session
->Profile
->dwType
>= WLX_PROFILE_TYPE_V2_0
)
457 ProfileInfo
.lpDefaultPath
= Session
->Profile
->pszNetworkDefaultUserProfile
;
458 ProfileInfo
.lpServerName
= Session
->Profile
->pszServerName
;
459 ProfileInfo
.lpPolicyPath
= Session
->Profile
->pszPolicy
;
462 if (!LoadUserProfileW(Session
->UserToken
, &ProfileInfo
))
464 ERR("WL: LoadUserProfileW() failed\n");
469 /* Create environment block for the user */
470 if (!CreateUserEnvironment(Session
))
472 WARN("WL: SetUserEnvironment() failed\n");
476 CallNotificationDlls(Session
, LogonHandler
);
478 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_APPLYINGYOURPERSONALSETTINGS
);
479 UpdatePerUserSystemParameters(0, TRUE
);
481 /* Set default user language */
482 if (!SetDefaultLanguage(Session
))
484 WARN("WL: SetDefaultLanguage() failed\n");
488 if (!StartUserShell(Session
))
490 //WCHAR StatusMsg[256];
491 WARN("WL: WlxActivateUserShell() failed\n");
492 //LoadStringW(hAppInstance, IDS_FAILEDACTIVATEUSERSHELL, StatusMsg, sizeof(StatusMsg) / sizeof(StatusMsg[0]));
493 //MessageBoxW(0, StatusMsg, NULL, MB_ICONERROR);
497 CallNotificationDlls(Session
, StartShellHandler
);
499 if (!InitializeScreenSaver(Session
))
500 WARN("WL: Failed to initialize screen saver\n");
502 Session
->hProfileInfo
= ProfileInfo
.hProfile
;
504 /* Logon has successed. Play sound. */
505 PlayLogonSound(Session
);
510 if (Session
->Profile
)
512 HeapFree(GetProcessHeap(), 0, Session
->Profile
->pszProfile
);
513 HeapFree(GetProcessHeap(), 0, Session
->Profile
);
515 Session
->Profile
= NULL
;
516 if (!ret
&& ProfileInfo
.hProfile
!= INVALID_HANDLE_VALUE
)
518 UnloadUserProfile(Session
->UserToken
, ProfileInfo
.hProfile
);
520 RemoveStatusMessage(Session
);
523 CloseHandle(Session
->UserToken
);
524 Session
->UserToken
= NULL
;
529 SwitchDesktop(Session
->ApplicationDesktop
);
530 Session
->LogonState
= STATE_LOGGED_ON
;
540 LogoffShutdownThread(
544 PLOGOFF_SHUTDOWN_DATA LSData
= (PLOGOFF_SHUTDOWN_DATA
)Parameter
;
547 if (LSData
->Session
->UserToken
!= NULL
&&
548 !ImpersonateLoggedOnUser(LSData
->Session
->UserToken
))
550 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
554 // FIXME: To be really fixed: need to check what needs to be kept and what needs to be removed there.
556 // uFlags = EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) |
557 // ((LSData->Flags & EWX_ACTION_MASK) == EWX_LOGOFF ? EWX_CALLER_WINLOGON_LOGOFF : 0);
559 uFlags
= EWX_CALLER_WINLOGON
| (LSData
->Flags
& 0x0F);
561 TRACE("In LogoffShutdownThread with uFlags == 0x%x; exit_in_progress == %s\n",
562 uFlags
, ExitReactOSInProgress
? "true" : "false");
564 ExitReactOSInProgress
= TRUE
;
566 /* Close processes of the interactive user */
567 if (!ExitWindowsEx(uFlags
, 0))
569 ERR("Unable to kill user apps, error %lu\n", GetLastError());
573 if (LSData
->Session
->UserToken
)
586 PLOGOFF_SHUTDOWN_DATA LSData
= (PLOGOFF_SHUTDOWN_DATA
)Parameter
;
588 TRACE("In KillComProcesses\n");
590 if (LSData
->Session
->UserToken
!= NULL
&&
591 !ImpersonateLoggedOnUser(LSData
->Session
->UserToken
))
593 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
597 /* Attempt to kill remaining processes. No notifications needed. */
598 if (!ExitWindowsEx(EWX_CALLER_WINLOGON
| EWX_NONOTIFY
| EWX_FORCE
| EWX_LOGOFF
, 0))
600 ERR("Unable to kill COM apps, error %lu\n", GetLastError());
604 if (LSData
->Session
->UserToken
)
612 CreateLogoffSecurityAttributes(
613 OUT PSECURITY_ATTRIBUTES
* ppsa
)
615 /* The following code is not working yet and messy */
616 /* Still, it gives some ideas about data types and functions involved and */
617 /* required to set up a SECURITY_DESCRIPTOR for a SECURITY_ATTRIBUTES */
618 /* instance for a thread, to allow that thread to ImpersonateLoggedOnUser(). */
619 /* Specifically THREAD_SET_THREAD_TOKEN is required. */
620 PSECURITY_DESCRIPTOR SecurityDescriptor
= NULL
;
621 PSECURITY_ATTRIBUTES psa
= 0;
624 EXPLICIT_ACCESS Access
;
625 PSID pEveryoneSID
= NULL
;
626 static SID_IDENTIFIER_AUTHORITY WorldAuthority
= { SECURITY_WORLD_SID_AUTHORITY
};
630 // Let's first try to enumerate what kind of data we need for this to ever work:
631 // 1. The Winlogon SID, to be able to give it THREAD_SET_THREAD_TOKEN.
632 // 2. The users SID (the user trying to logoff, or rather shut down the system).
633 // 3. At least two EXPLICIT_ACCESS instances:
634 // 3.1 One for Winlogon itself, giving it the rights
635 // required to THREAD_SET_THREAD_TOKEN (as it's needed to successfully call
636 // ImpersonateLoggedOnUser).
637 // 3.2 One for the user, to allow *that* thread to perform its work.
638 // 4. An ACL to hold the these EXPLICIT_ACCESS ACE's.
639 // 5. A SECURITY_DESCRIPTOR to hold the ACL, and finally.
640 // 6. A SECURITY_ATTRIBUTES instance to pull all of this required stuff
641 // together, to hand it to CreateThread.
643 // However, it seems struct LOGOFF_SHUTDOWN_DATA doesn't contain
644 // these required SID's, why they'd have to be added.
645 // The Winlogon's own SID should probably only be created once,
646 // while the user's SID obviously must be created for each new user.
647 // Might as well store it when the user logs on?
649 if(!AllocateAndInitializeSid(&WorldAuthority
,
655 ERR("Failed to initialize security descriptor for logoff thread!\n");
656 return STATUS_UNSUCCESSFUL
;
659 /* set up the required security attributes to be able to shut down */
660 /* To save space and time, allocate a single block of memory holding */
661 /* both SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR */
662 pMem
= HeapAlloc(GetProcessHeap(),
664 sizeof(SECURITY_ATTRIBUTES
) +
665 SECURITY_DESCRIPTOR_MIN_LENGTH
+
669 ERR("Failed to allocate memory for logoff security descriptor!\n");
670 return STATUS_NO_MEMORY
;
673 /* Note that the security descriptor needs to be in _absolute_ format, */
674 /* meaning its members must be pointers to other structures, rather */
675 /* than the relative format using offsets */
676 psa
= (PSECURITY_ATTRIBUTES
)pMem
;
677 SecurityDescriptor
= (PSECURITY_DESCRIPTOR
)(pMem
+ sizeof(SECURITY_ATTRIBUTES
));
678 pACL
= (PACL
)(((PBYTE
)SecurityDescriptor
) + SECURITY_DESCRIPTOR_MIN_LENGTH
);
680 // Initialize an EXPLICIT_ACCESS structure for an ACE.
681 // The ACE will allow this thread to log off (and shut down the system, currently).
682 ZeroMemory(&Access
, sizeof(Access
));
683 Access
.grfAccessPermissions
= THREAD_SET_THREAD_TOKEN
;
684 Access
.grfAccessMode
= SET_ACCESS
; // GRANT_ACCESS?
685 Access
.grfInheritance
= NO_INHERITANCE
;
686 Access
.Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
687 Access
.Trustee
.TrusteeType
= TRUSTEE_IS_WELL_KNOWN_GROUP
;
688 Access
.Trustee
.ptstrName
= pEveryoneSID
;
690 if (SetEntriesInAcl(1, &Access
, NULL
, &pACL
) != ERROR_SUCCESS
)
692 ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n");
694 HeapFree(GetProcessHeap(), 0, pMem
);
695 return STATUS_UNSUCCESSFUL
;
698 if (!InitializeSecurityDescriptor(SecurityDescriptor
, SECURITY_DESCRIPTOR_REVISION
))
700 ERR("Failed to initialize security descriptor for logoff thread!\n");
701 HeapFree(GetProcessHeap(), 0, pMem
);
702 return STATUS_UNSUCCESSFUL
;
705 if (!SetSecurityDescriptorDacl(SecurityDescriptor
,
706 TRUE
, // bDaclPresent flag
708 FALSE
)) // not a default DACL
710 ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
711 HeapFree(GetProcessHeap(), 0, pMem
);
712 return STATUS_UNSUCCESSFUL
;
715 psa
->nLength
= sizeof(SECURITY_ATTRIBUTES
);
716 psa
->lpSecurityDescriptor
= SecurityDescriptor
;
717 psa
->bInheritHandle
= FALSE
;
721 return STATUS_SUCCESS
;
726 DestroyLogoffSecurityAttributes(
727 IN PSECURITY_ATTRIBUTES psa
)
731 HeapFree(GetProcessHeap(), 0, psa
);
739 IN OUT PWLSESSION Session
,
742 PLOGOFF_SHUTDOWN_DATA LSData
;
743 PSECURITY_ATTRIBUTES psa
;
748 /* Prepare data for logoff thread */
749 LSData
= HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA
));
752 ERR("Failed to allocate mem for thread data\n");
753 return STATUS_NO_MEMORY
;
755 LSData
->Flags
= Flags
;
756 LSData
->Session
= Session
;
758 Status
= CreateLogoffSecurityAttributes(&psa
);
759 if (!NT_SUCCESS(Status
))
761 ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status
);
762 HeapFree(GetProcessHeap(), 0, LSData
);
766 /* Run logoff thread */
767 hThread
= CreateThread(psa
, 0, LogoffShutdownThread
, (LPVOID
)LSData
, 0, NULL
);
770 ERR("Unable to create logoff thread, error %lu\n", GetLastError());
771 DestroyLogoffSecurityAttributes(psa
);
772 HeapFree(GetProcessHeap(), 0, LSData
);
773 return STATUS_UNSUCCESSFUL
;
775 WaitForSingleObject(hThread
, INFINITE
);
776 if (!GetExitCodeThread(hThread
, &exitCode
))
778 ERR("Unable to get exit code of logoff thread (error %lu)\n", GetLastError());
779 CloseHandle(hThread
);
780 DestroyLogoffSecurityAttributes(psa
);
781 HeapFree(GetProcessHeap(), 0, LSData
);
782 return STATUS_UNSUCCESSFUL
;
784 CloseHandle(hThread
);
787 ERR("Logoff thread returned failure\n");
788 DestroyLogoffSecurityAttributes(psa
);
789 HeapFree(GetProcessHeap(), 0, LSData
);
790 return STATUS_UNSUCCESSFUL
;
793 SwitchDesktop(Session
->WinlogonDesktop
);
795 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOGGINGOFF);
797 // FIXME: Closing network connections!
798 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_CLOSINGNETWORKCONNECTIONS);
800 /* Kill remaining COM apps. Only at logoff! */
801 hThread
= CreateThread(psa
, 0, KillComProcesses
, (LPVOID
)LSData
, 0, NULL
);
804 WaitForSingleObject(hThread
, INFINITE
);
805 CloseHandle(hThread
);
808 /* We're done with the SECURITY_DESCRIPTOR */
809 DestroyLogoffSecurityAttributes(psa
);
812 HeapFree(GetProcessHeap(), 0, LSData
);
814 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_SAVEYOURSETTINGS
);
816 UnloadUserProfile(Session
->UserToken
, Session
->hProfileInfo
);
818 CallNotificationDlls(Session
, LogoffHandler
);
820 CloseHandle(Session
->UserToken
);
821 UpdatePerUserSystemParameters(0, FALSE
);
822 Session
->LogonState
= STATE_LOGGED_OFF
;
823 Session
->UserToken
= NULL
;
825 return STATUS_SUCCESS
;
831 ShutdownComputerWindowProc(
837 UNREFERENCED_PARAMETER(lParam
);
843 switch (LOWORD(wParam
))
845 case IDC_BTNSHTDOWNCOMPUTER
:
846 EndDialog(hwndDlg
, IDC_BTNSHTDOWNCOMPUTER
);
853 RemoveMenu(GetSystemMenu(hwndDlg
, FALSE
), SC_CLOSE
, MF_BYCOMMAND
);
854 SetFocus(GetDlgItem(hwndDlg
, IDC_BTNSHTDOWNCOMPUTER
));
864 IN OUT PWLSESSION Session
)
866 if (Session
->SASWindow
)
868 DestroyWindow(Session
->SASWindow
);
869 Session
->SASWindow
= NULL
;
871 if (Session
->hEndOfScreenSaverThread
)
872 SetEvent(Session
->hEndOfScreenSaverThread
);
873 UnregisterClassW(WINLOGON_SAS_CLASS
, hAppInstance
);
878 IN OUT PWLSESSION Session
,
881 PLOGOFF_SHUTDOWN_DATA LSData
;
886 // SwitchDesktop(Session->WinlogonDesktop);
887 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_REACTOSISSHUTTINGDOWN
);
889 /* Prepare data for shutdown thread */
890 LSData
= HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA
));
893 ERR("Failed to allocate mem for thread data\n");
894 return STATUS_NO_MEMORY
;
896 if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
)
897 LSData
->Flags
= EWX_POWEROFF
;
898 else if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_REBOOT
)
899 LSData
->Flags
= EWX_REBOOT
;
901 LSData
->Flags
= EWX_SHUTDOWN
;
902 LSData
->Session
= Session
;
904 // FIXME: We may need to specify this flag to really force application kill
905 // (we are shutting down ReactOS, not just logging off so no hangs, etc...
906 // should be allowed).
907 // LSData->Flags |= EWX_FORCE;
909 /* Run shutdown thread */
910 hThread
= CreateThread(NULL
, 0, LogoffShutdownThread
, (LPVOID
)LSData
, 0, NULL
);
913 ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
914 HeapFree(GetProcessHeap(), 0, LSData
);
915 return STATUS_UNSUCCESSFUL
;
917 WaitForSingleObject(hThread
, INFINITE
);
918 HeapFree(GetProcessHeap(), 0, LSData
);
919 if (!GetExitCodeThread(hThread
, &exitCode
))
921 ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError());
922 CloseHandle(hThread
);
923 return STATUS_UNSUCCESSFUL
;
925 CloseHandle(hThread
);
928 ERR("Shutdown thread returned failure\n");
929 return STATUS_UNSUCCESSFUL
;
932 CallNotificationDlls(Session
, ShutdownHandler
);
934 /* Destroy SAS window */
935 UninitializeSAS(Session
);
937 /* Now we can shut down NT */
938 ERR("Shutting down NT...\n");
939 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, TRUE
, FALSE
, &Old
);
940 if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_REBOOT
)
942 NtShutdownSystem(ShutdownReboot
);
948 /* FIXME - only show this dialog if it's a shutdown and the computer doesn't support APM */
949 DialogBox(hAppInstance
, MAKEINTRESOURCE(IDD_SHUTDOWNCOMPUTER
),
950 GetDesktopWindow(), ShutdownComputerWindowProc
);
952 NtShutdownSystem(ShutdownNoReboot
);
954 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, Old
, FALSE
, &Old
);
955 return STATUS_SUCCESS
;
961 IN OUT PWLSESSION Session
,
966 case WLX_SAS_ACTION_LOGON
: /* 0x01 */
967 if (Session
->LogonState
== STATE_LOGGED_OFF_SAS
)
969 if (!HandleLogon(Session
))
971 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
972 CallNotificationDlls(Session
, LogonHandler
);
976 case WLX_SAS_ACTION_NONE
: /* 0x02 */
977 if (Session
->LogonState
== STATE_LOGGED_OFF_SAS
)
979 Session
->LogonState
= STATE_LOGGED_OFF
;
980 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
982 else if (Session
->LogonState
== STATE_LOGGED_ON_SAS
)
984 Session
->LogonState
= STATE_LOGGED_ON
;
986 else if (Session
->LogonState
== STATE_LOCKED_SAS
)
988 Session
->LogonState
= STATE_LOCKED
;
989 Session
->Gina
.Functions
.WlxDisplayLockedNotice(Session
->Gina
.Context
);
992 case WLX_SAS_ACTION_LOCK_WKSTA
: /* 0x03 */
993 if (Session
->Gina
.Functions
.WlxIsLockOk(Session
->Gina
.Context
))
995 SwitchDesktop(Session
->WinlogonDesktop
);
996 Session
->LogonState
= STATE_LOCKED
;
997 Session
->Gina
.Functions
.WlxDisplayLockedNotice(Session
->Gina
.Context
);
998 CallNotificationDlls(Session
, LockHandler
);
1001 case WLX_SAS_ACTION_LOGOFF
: /* 0x04 */
1002 case WLX_SAS_ACTION_SHUTDOWN
: /* 0x05 */
1003 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
: /* 0x0a */
1004 case WLX_SAS_ACTION_SHUTDOWN_REBOOT
: /* 0x0b */
1005 if (Session
->LogonState
!= STATE_LOGGED_OFF
)
1007 if (!Session
->Gina
.Functions
.WlxIsLogoffOk(Session
->Gina
.Context
))
1009 if (!NT_SUCCESS(HandleLogoff(Session
, EWX_LOGOFF
)))
1011 RemoveStatusMessage(Session
);
1014 Session
->Gina
.Functions
.WlxLogoff(Session
->Gina
.Context
);
1016 if (WLX_SHUTTINGDOWN(wlxAction
))
1018 // FIXME: WlxShutdown should be done from inside HandleShutdown,
1019 // after having displayed "ReactOS is shutting down" message.
1020 Session
->Gina
.Functions
.WlxShutdown(Session
->Gina
.Context
, wlxAction
);
1021 if (!NT_SUCCESS(HandleShutdown(Session
, wlxAction
)))
1023 RemoveStatusMessage(Session
);
1024 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1029 RemoveStatusMessage(Session
);
1030 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1033 case WLX_SAS_ACTION_TASKLIST
: /* 0x07 */
1034 SwitchDesktop(Session
->ApplicationDesktop
);
1035 Session
->LogonState
= STATE_LOGGED_ON
;
1036 StartTaskManager(Session
);
1038 case WLX_SAS_ACTION_UNLOCK_WKSTA
: /* 0x08 */
1039 SwitchDesktop(Session
->ApplicationDesktop
);
1040 Session
->LogonState
= STATE_LOGGED_ON
;
1041 CallNotificationDlls(Session
, UnlockHandler
);
1044 WARN("Unknown SAS action 0x%lx\n", wlxAction
);
1048 DWORD WINAPI
SetWindowStationUser(HWINSTA hWinSta
, LUID
* pluid
, PSID psid
, DWORD sidSize
);
1051 AddAceToWindowStation(
1056 BOOL
AllowWinstaAccess(PWLSESSION Session
)
1058 BOOL bSuccess
= FALSE
;
1061 PTOKEN_GROUPS ptg
= NULL
;
1063 TOKEN_STATISTICS Stats
;
1067 // Get required buffer size and allocate the TOKEN_GROUPS buffer.
1069 if (!GetTokenInformation(Session
->UserToken
,
1075 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
1078 ptg
= (PTOKEN_GROUPS
)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, dwLength
);
1083 // Get the token group information from the access token.
1084 if (!GetTokenInformation(Session
->UserToken
,
1093 // Loop through the groups to find the logon SID.
1095 for (dwIndex
= 0; dwIndex
< ptg
->GroupCount
; dwIndex
++)
1097 if ((ptg
->Groups
[dwIndex
].Attributes
& SE_GROUP_LOGON_ID
)
1098 == SE_GROUP_LOGON_ID
)
1100 psid
= ptg
->Groups
[dwIndex
].Sid
;
1105 dwLength
= GetLengthSid(psid
);
1107 if (!GetTokenInformation(Session
->UserToken
,
1110 sizeof(TOKEN_STATISTICS
),
1113 WARN("Couldn't get Authentication id from user token!\n");
1117 AddAceToWindowStation(Session
->InteractiveWindowStation
, psid
);
1119 ret
= SetWindowStationUser(Session
->InteractiveWindowStation
,
1120 &Stats
.AuthenticationId
,
1123 TRACE("SetWindowStationUser returned 0x%x\n", ret
);
1129 // Free the buffer for the token groups.
1131 HeapFree(GetProcessHeap(), 0, (LPVOID
)ptg
);
1139 IN OUT PWLSESSION Session
,
1142 DWORD wlxAction
= WLX_SAS_ACTION_NONE
;
1143 PSID LogonSid
= NULL
; /* FIXME */
1144 BOOL bSecure
= TRUE
;
1148 case WLX_SAS_TYPE_CTRL_ALT_DEL
:
1149 switch (Session
->LogonState
)
1152 Session
->LogonState
= STATE_LOGGED_OFF
;
1153 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1156 case STATE_LOGGED_OFF
:
1157 Session
->LogonState
= STATE_LOGGED_OFF_SAS
;
1159 CloseAllDialogWindows();
1161 Session
->Options
= 0;
1163 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxLoggedOutSAS(
1164 Session
->Gina
.Context
,
1169 &Session
->UserToken
,
1170 &Session
->MprNotifyInfo
,
1171 (PVOID
*)&Session
->Profile
);
1173 AllowWinstaAccess(Session
);
1176 case STATE_LOGGED_OFF_SAS
:
1177 /* Ignore SAS if we are already in an SAS state */
1180 case STATE_LOGGED_ON
:
1181 Session
->LogonState
= STATE_LOGGED_ON_SAS
;
1182 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxLoggedOnSAS(Session
->Gina
.Context
, dwSasType
, NULL
);
1185 case STATE_LOGGED_ON_SAS
:
1186 /* Ignore SAS if we are already in an SAS state */
1190 Session
->LogonState
= STATE_LOCKED_SAS
;
1192 CloseAllDialogWindows();
1194 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxWkstaLockedSAS(Session
->Gina
.Context
, dwSasType
);
1197 case STATE_LOCKED_SAS
:
1198 /* Ignore SAS if we are already in an SAS state */
1206 case WLX_SAS_TYPE_TIMEOUT
:
1209 case WLX_SAS_TYPE_SCRNSVR_TIMEOUT
:
1210 if (!Session
->Gina
.Functions
.WlxScreenSaverNotify(Session
->Gina
.Context
, &bSecure
))
1212 /* Skip start of screen saver */
1213 SetEvent(Session
->hEndOfScreenSaver
);
1217 StartScreenSaver(Session
);
1220 wlxAction
= WLX_SAS_ACTION_LOCK_WKSTA
;
1221 // DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
1226 case WLX_SAS_TYPE_SCRNSVR_ACTIVITY
:
1227 SetEvent(Session
->hUserActivity
);
1231 DoGenericAction(Session
, wlxAction
);
1237 IN PWLSESSION Session
,
1240 /* Register Ctrl+Alt+Del Hotkey */
1241 if (!RegisterHotKey(hwndSAS
, HK_CTRL_ALT_DEL
, MOD_CONTROL
| MOD_ALT
, VK_DELETE
))
1243 ERR("WL: Unable to register Ctrl+Alt+Del hotkey!\n");
1247 /* Register Ctrl+Shift+Esc (optional) */
1248 Session
->TaskManHotkey
= RegisterHotKey(hwndSAS
, HK_CTRL_SHIFT_ESC
, MOD_CONTROL
| MOD_SHIFT
, VK_ESCAPE
);
1249 if (!Session
->TaskManHotkey
)
1250 WARN("WL: Warning: Unable to register Ctrl+Alt+Esc hotkey!\n");
1257 IN PWLSESSION Session
,
1260 /* Unregister hotkeys */
1261 UnregisterHotKey(hwndSAS
, HK_CTRL_ALT_DEL
);
1263 if (Session
->TaskManHotkey
)
1264 UnregisterHotKey(hwndSAS
, HK_CTRL_SHIFT_ESC
);
1271 HandleMessageBeep(UINT uType
)
1281 EventName
= L
"SystemDefault";
1283 case MB_ICONASTERISK
:
1284 EventName
= L
"SystemAsterisk";
1286 case MB_ICONEXCLAMATION
:
1287 EventName
= L
"SystemExclamation";
1290 EventName
= L
"SystemHand";
1292 case MB_ICONQUESTION
:
1293 EventName
= L
"SystemQuestion";
1296 WARN("Unhandled type %d\n", uType
);
1297 EventName
= L
"SystemDefault";
1300 return PlaySoundRoutine(EventName
, FALSE
, SND_ALIAS
| SND_NOWAIT
| SND_NOSTOP
| SND_ASYNC
);
1312 PWLSESSION Session
= (PWLSESSION
)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1320 case MAKELONG(MOD_CONTROL
| MOD_ALT
, VK_DELETE
):
1322 TRACE("SAS: CONTROL+ALT+DELETE\n");
1323 if (!Session
->Gina
.UseCtrlAltDelete
)
1325 PostMessageW(Session
->SASWindow
, WLX_WM_SAS
, WLX_SAS_TYPE_CTRL_ALT_DEL
, 0);
1328 case MAKELONG(MOD_CONTROL
| MOD_SHIFT
, VK_ESCAPE
):
1330 TRACE("SAS: CONTROL+SHIFT+ESCAPE\n");
1331 if (Session
->LogonState
== STATE_LOGGED_ON
)
1332 DoGenericAction(Session
, WLX_SAS_ACTION_TASKLIST
);
1340 /* Get the session pointer from the create data */
1341 Session
= (PWLSESSION
)((LPCREATESTRUCT
)lParam
)->lpCreateParams
;
1343 /* Save the Session pointer */
1344 SetWindowLongPtrW(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)Session
);
1347 return RegisterHotKeys(Session
, hwndDlg
);
1351 if (!GetSetupType())
1352 UnregisterHotKeys(Session
, hwndDlg
);
1355 case WM_SETTINGCHANGE
:
1357 UINT uiAction
= (UINT
)wParam
;
1358 if (uiAction
== SPI_SETSCREENSAVETIMEOUT
1359 || uiAction
== SPI_SETSCREENSAVEACTIVE
)
1361 SetEvent(Session
->hScreenSaverParametersChanged
);
1365 case WM_LOGONNOTIFY
:
1369 case LN_MESSAGE_BEEP
:
1371 return HandleMessageBeep(lParam
);
1373 case LN_SHELL_EXITED
:
1375 /* lParam is the exit code */
1377 Session
->LogonState
!= STATE_LOGGED_OFF
&&
1378 Session
->LogonState
!= STATE_LOGGED_OFF_SAS
)
1380 SetTimer(hwndDlg
, 1, 1000, NULL
);
1384 case LN_START_SCREENSAVE
:
1386 DispatchSAS(Session
, WLX_SAS_TYPE_SCRNSVR_TIMEOUT
);
1389 case LN_LOCK_WORKSTATION
:
1391 DoGenericAction(Session
, WLX_SAS_ACTION_LOCK_WKSTA
);
1396 UINT Flags
= (UINT
)lParam
;
1397 UINT Action
= Flags
& EWX_ACTION_MASK
;
1400 TRACE("\tFlags : 0x%lx\n", lParam
);
1403 * Our caller (USERSRV) should have added the shutdown flag
1404 * when setting also poweroff or reboot.
1406 if (Action
& (EWX_POWEROFF
| EWX_REBOOT
))
1408 if ((Action
& EWX_SHUTDOWN
) == 0)
1410 ERR("Missing EWX_SHUTDOWN flag for poweroff or reboot; action 0x%x\n", Action
);
1411 return STATUS_INVALID_PARAMETER
;
1414 /* Now we can locally remove it for performing checks */
1415 Action
&= ~EWX_SHUTDOWN
;
1418 /* Check parameters */
1419 if (Action
& EWX_FORCE
)
1422 ERR("FIXME: EWX_FORCE present for Winlogon, what to do?\n");
1423 Action
&= ~EWX_FORCE
;
1428 wlxAction
= WLX_SAS_ACTION_LOGOFF
;
1431 wlxAction
= WLX_SAS_ACTION_SHUTDOWN
;
1434 wlxAction
= WLX_SAS_ACTION_SHUTDOWN_REBOOT
;
1437 wlxAction
= WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
;
1442 ERR("Invalid ExitWindows action 0x%x\n", Action
);
1443 return STATUS_INVALID_PARAMETER
;
1447 TRACE("In LN_LOGOFF, exit_in_progress == %s\n",
1448 ExitReactOSInProgress
? "true" : "false");
1451 * In case a parallel shutdown request is done (while we are
1452 * being to shut down) and it was not done by Winlogon itself,
1453 * then just stop here.
1456 // This code is commented at the moment (even if it's correct) because
1457 // our log-offs do not really work: the shell is restarted, no app is killed
1458 // etc... and as a result you just get explorer opening "My Documents". And
1459 // if you try now a shut down, it won't work because winlogon thinks it is
1460 // still in the middle of a shutdown.
1461 // Maybe we also need to reset ExitReactOSInProgress somewhere else??
1462 if (ExitReactOSInProgress
&& (lParam
& EWX_CALLER_WINLOGON
) == 0)
1467 /* Now do the shutdown action proper */
1468 DoGenericAction(Session
, wlxAction
);
1471 case LN_LOGOFF_CANCELED
:
1473 ERR("Logoff canceled!!, before: exit_in_progress == %s, after will be false\n",
1474 ExitReactOSInProgress
? "true" : "false");
1476 ExitReactOSInProgress
= FALSE
;
1481 ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam
);
1490 KillTimer(hwndDlg
, 1);
1491 StartUserShell(Session
);
1497 DispatchSAS(Session
, (DWORD
)wParam
);
1502 return DefWindowProc(hwndDlg
, uMsg
, wParam
, lParam
);
1507 IN OUT PWLSESSION Session
)
1512 if (!SwitchDesktop(Session
->WinlogonDesktop
))
1514 ERR("WL: Failed to switch to winlogon desktop\n");
1518 /* Register SAS window class */
1519 swc
.cbSize
= sizeof(WNDCLASSEXW
);
1520 swc
.style
= CS_SAVEBITS
;
1521 swc
.lpfnWndProc
= SASWindowProc
;
1524 swc
.hInstance
= hAppInstance
;
1527 swc
.hbrBackground
= NULL
;
1528 swc
.lpszMenuName
= NULL
;
1529 swc
.lpszClassName
= WINLOGON_SAS_CLASS
;
1531 if (RegisterClassExW(&swc
) == 0)
1533 ERR("WL: Failed to register SAS window class\n");
1537 /* Create invisible SAS window */
1538 Session
->SASWindow
= CreateWindowExW(
1544 hAppInstance
, Session
);
1545 if (!Session
->SASWindow
)
1547 ERR("WL: Failed to create SAS window\n");
1551 /* Register SAS window to receive SAS notifications */
1552 if (!SetLogonNotifyWindow(Session
->SASWindow
))
1554 ERR("WL: Failed to register SAS window\n");
1558 if (!SetDefaultLanguage(NULL
))
1565 UninitializeSAS(Session
);