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
);
513 RestoreAllConnections(PWLSESSION Session
)
518 DWORD dSize
= 0x1000;
523 UserProfile
= (Session
&& Session
->UserToken
);
529 if (!ImpersonateLoggedOnUser(Session
->UserToken
))
531 ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
535 dRet
= WNetOpenEnum(RESOURCE_REMEMBERED
, RESOURCETYPE_DISK
, 0, NULL
, &hEnum
);
536 if (dRet
!= WN_SUCCESS
)
538 ERR("Failed to open enumeration: %lu\n", dRet
);
542 lpRes
= HeapAlloc(GetProcessHeap(), 0, dSize
);
545 ERR("Failed to allocate memory\n");
546 WNetCloseEnum(hEnum
);
555 memset(lpRes
, 0, dSize
);
556 dRet
= WNetEnumResource(hEnum
, &dCount
, lpRes
, &dSize
);
557 if (dRet
== WN_SUCCESS
|| dRet
== WN_MORE_DATA
)
560 for (; dCount
; dCount
--)
562 WNetAddConnection(lpCur
->lpRemoteName
, NULL
, lpCur
->lpLocalName
);
566 } while (dRet
!= WN_NO_MORE_ENTRIES
);
568 HeapFree(GetProcessHeap(), 0, lpRes
);
569 WNetCloseEnum(hEnum
);
578 IN OUT PWLSESSION Session
)
580 PROFILEINFOW ProfileInfo
;
583 /* Loading personal settings */
584 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_LOADINGYOURPERSONALSETTINGS
);
585 ProfileInfo
.hProfile
= INVALID_HANDLE_VALUE
;
586 if (0 == (Session
->Options
& WLX_LOGON_OPT_NO_PROFILE
))
588 if (Session
->Profile
== NULL
589 || (Session
->Profile
->dwType
!= WLX_PROFILE_TYPE_V1_0
590 && Session
->Profile
->dwType
!= WLX_PROFILE_TYPE_V2_0
))
592 ERR("WL: Wrong profile\n");
596 /* Load the user profile */
597 ZeroMemory(&ProfileInfo
, sizeof(PROFILEINFOW
));
598 ProfileInfo
.dwSize
= sizeof(PROFILEINFOW
);
599 ProfileInfo
.dwFlags
= 0;
600 ProfileInfo
.lpUserName
= Session
->MprNotifyInfo
.pszUserName
;
601 ProfileInfo
.lpProfilePath
= Session
->Profile
->pszProfile
;
602 if (Session
->Profile
->dwType
>= WLX_PROFILE_TYPE_V2_0
)
604 ProfileInfo
.lpDefaultPath
= Session
->Profile
->pszNetworkDefaultUserProfile
;
605 ProfileInfo
.lpServerName
= Session
->Profile
->pszServerName
;
606 ProfileInfo
.lpPolicyPath
= Session
->Profile
->pszPolicy
;
609 if (!LoadUserProfileW(Session
->UserToken
, &ProfileInfo
))
611 ERR("WL: LoadUserProfileW() failed\n");
616 /* Create environment block for the user */
617 if (!CreateUserEnvironment(Session
))
619 WARN("WL: SetUserEnvironment() failed\n");
623 CallNotificationDlls(Session
, LogonHandler
);
625 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_APPLYINGYOURPERSONALSETTINGS
);
626 UpdatePerUserSystemParameters(0, TRUE
);
628 /* Set default user language */
629 if (!SetDefaultLanguage(Session
))
631 WARN("WL: SetDefaultLanguage() failed\n");
635 AllowWinstaAccess(Session
);
637 /* Connect remote resources */
638 RestoreAllConnections(Session
);
640 if (!StartUserShell(Session
))
642 //WCHAR StatusMsg[256];
643 WARN("WL: WlxActivateUserShell() failed\n");
644 //LoadStringW(hAppInstance, IDS_FAILEDACTIVATEUSERSHELL, StatusMsg, sizeof(StatusMsg) / sizeof(StatusMsg[0]));
645 //MessageBoxW(0, StatusMsg, NULL, MB_ICONERROR);
649 CallNotificationDlls(Session
, StartShellHandler
);
651 if (!InitializeScreenSaver(Session
))
652 WARN("WL: Failed to initialize screen saver\n");
654 Session
->hProfileInfo
= ProfileInfo
.hProfile
;
656 /* Logon has succeeded. Play sound. */
657 PlayLogonSound(Session
);
662 if (Session
->Profile
)
664 HeapFree(GetProcessHeap(), 0, Session
->Profile
->pszProfile
);
665 HeapFree(GetProcessHeap(), 0, Session
->Profile
);
667 Session
->Profile
= NULL
;
668 if (!ret
&& ProfileInfo
.hProfile
!= INVALID_HANDLE_VALUE
)
670 UnloadUserProfile(Session
->UserToken
, ProfileInfo
.hProfile
);
672 RemoveStatusMessage(Session
);
675 SetWindowStationUser(Session
->InteractiveWindowStation
,
677 CloseHandle(Session
->UserToken
);
678 Session
->UserToken
= NULL
;
683 SwitchDesktop(Session
->ApplicationDesktop
);
684 Session
->LogonState
= STATE_LOGGED_ON
;
694 LogoffShutdownThread(
698 PLOGOFF_SHUTDOWN_DATA LSData
= (PLOGOFF_SHUTDOWN_DATA
)Parameter
;
701 if (LSData
->Session
->UserToken
!= NULL
&&
702 !ImpersonateLoggedOnUser(LSData
->Session
->UserToken
))
704 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
708 // FIXME: To be really fixed: need to check what needs to be kept and what needs to be removed there.
710 // uFlags = EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) |
711 // ((LSData->Flags & EWX_ACTION_MASK) == EWX_LOGOFF ? EWX_CALLER_WINLOGON_LOGOFF : 0);
713 uFlags
= EWX_CALLER_WINLOGON
| (LSData
->Flags
& 0x0F);
715 TRACE("In LogoffShutdownThread with uFlags == 0x%x; exit_in_progress == %s\n",
716 uFlags
, ExitReactOSInProgress
? "true" : "false");
718 ExitReactOSInProgress
= TRUE
;
720 /* Close processes of the interactive user */
721 if (!ExitWindowsEx(uFlags
, 0))
723 ERR("Unable to kill user apps, error %lu\n", GetLastError());
727 /* Cancel all the user connections */
728 WNetClearConnections(0);
730 if (LSData
->Session
->UserToken
)
743 PLOGOFF_SHUTDOWN_DATA LSData
= (PLOGOFF_SHUTDOWN_DATA
)Parameter
;
745 TRACE("In KillComProcesses\n");
747 if (LSData
->Session
->UserToken
!= NULL
&&
748 !ImpersonateLoggedOnUser(LSData
->Session
->UserToken
))
750 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
754 /* Attempt to kill remaining processes. No notifications needed. */
755 if (!ExitWindowsEx(EWX_CALLER_WINLOGON
| EWX_NONOTIFY
| EWX_FORCE
| EWX_LOGOFF
, 0))
757 ERR("Unable to kill COM apps, error %lu\n", GetLastError());
761 if (LSData
->Session
->UserToken
)
769 CreateLogoffSecurityAttributes(
770 OUT PSECURITY_ATTRIBUTES
* ppsa
)
772 /* The following code is not working yet and messy */
773 /* Still, it gives some ideas about data types and functions involved and */
774 /* required to set up a SECURITY_DESCRIPTOR for a SECURITY_ATTRIBUTES */
775 /* instance for a thread, to allow that thread to ImpersonateLoggedOnUser(). */
776 /* Specifically THREAD_SET_THREAD_TOKEN is required. */
777 PSECURITY_DESCRIPTOR SecurityDescriptor
= NULL
;
778 PSECURITY_ATTRIBUTES psa
= 0;
781 EXPLICIT_ACCESS Access
;
782 PSID pEveryoneSID
= NULL
;
783 static SID_IDENTIFIER_AUTHORITY WorldAuthority
= { SECURITY_WORLD_SID_AUTHORITY
};
787 // Let's first try to enumerate what kind of data we need for this to ever work:
788 // 1. The Winlogon SID, to be able to give it THREAD_SET_THREAD_TOKEN.
789 // 2. The users SID (the user trying to logoff, or rather shut down the system).
790 // 3. At least two EXPLICIT_ACCESS instances:
791 // 3.1 One for Winlogon itself, giving it the rights
792 // required to THREAD_SET_THREAD_TOKEN (as it's needed to successfully call
793 // ImpersonateLoggedOnUser).
794 // 3.2 One for the user, to allow *that* thread to perform its work.
795 // 4. An ACL to hold the these EXPLICIT_ACCESS ACE's.
796 // 5. A SECURITY_DESCRIPTOR to hold the ACL, and finally.
797 // 6. A SECURITY_ATTRIBUTES instance to pull all of this required stuff
798 // together, to hand it to CreateThread.
800 // However, it seems struct LOGOFF_SHUTDOWN_DATA doesn't contain
801 // these required SID's, why they'd have to be added.
802 // The Winlogon's own SID should probably only be created once,
803 // while the user's SID obviously must be created for each new user.
804 // Might as well store it when the user logs on?
806 if(!AllocateAndInitializeSid(&WorldAuthority
,
812 ERR("Failed to initialize security descriptor for logoff thread!\n");
813 return STATUS_UNSUCCESSFUL
;
816 /* set up the required security attributes to be able to shut down */
817 /* To save space and time, allocate a single block of memory holding */
818 /* both SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR */
819 pMem
= HeapAlloc(GetProcessHeap(),
821 sizeof(SECURITY_ATTRIBUTES
) +
822 SECURITY_DESCRIPTOR_MIN_LENGTH
+
826 ERR("Failed to allocate memory for logoff security descriptor!\n");
827 return STATUS_NO_MEMORY
;
830 /* Note that the security descriptor needs to be in _absolute_ format, */
831 /* meaning its members must be pointers to other structures, rather */
832 /* than the relative format using offsets */
833 psa
= (PSECURITY_ATTRIBUTES
)pMem
;
834 SecurityDescriptor
= (PSECURITY_DESCRIPTOR
)(pMem
+ sizeof(SECURITY_ATTRIBUTES
));
835 pACL
= (PACL
)(((PBYTE
)SecurityDescriptor
) + SECURITY_DESCRIPTOR_MIN_LENGTH
);
837 // Initialize an EXPLICIT_ACCESS structure for an ACE.
838 // The ACE will allow this thread to log off (and shut down the system, currently).
839 ZeroMemory(&Access
, sizeof(Access
));
840 Access
.grfAccessPermissions
= THREAD_SET_THREAD_TOKEN
;
841 Access
.grfAccessMode
= SET_ACCESS
; // GRANT_ACCESS?
842 Access
.grfInheritance
= NO_INHERITANCE
;
843 Access
.Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
844 Access
.Trustee
.TrusteeType
= TRUSTEE_IS_WELL_KNOWN_GROUP
;
845 Access
.Trustee
.ptstrName
= pEveryoneSID
;
847 if (SetEntriesInAcl(1, &Access
, NULL
, &pACL
) != ERROR_SUCCESS
)
849 ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n");
851 HeapFree(GetProcessHeap(), 0, pMem
);
852 return STATUS_UNSUCCESSFUL
;
855 if (!InitializeSecurityDescriptor(SecurityDescriptor
, SECURITY_DESCRIPTOR_REVISION
))
857 ERR("Failed to initialize security descriptor for logoff thread!\n");
858 HeapFree(GetProcessHeap(), 0, pMem
);
859 return STATUS_UNSUCCESSFUL
;
862 if (!SetSecurityDescriptorDacl(SecurityDescriptor
,
863 TRUE
, // bDaclPresent flag
865 FALSE
)) // not a default DACL
867 ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
868 HeapFree(GetProcessHeap(), 0, pMem
);
869 return STATUS_UNSUCCESSFUL
;
872 psa
->nLength
= sizeof(SECURITY_ATTRIBUTES
);
873 psa
->lpSecurityDescriptor
= SecurityDescriptor
;
874 psa
->bInheritHandle
= FALSE
;
878 return STATUS_SUCCESS
;
883 DestroyLogoffSecurityAttributes(
884 IN PSECURITY_ATTRIBUTES psa
)
888 HeapFree(GetProcessHeap(), 0, psa
);
896 IN OUT PWLSESSION Session
,
899 PLOGOFF_SHUTDOWN_DATA LSData
;
900 PSECURITY_ATTRIBUTES psa
;
905 /* Prepare data for logoff thread */
906 LSData
= HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA
));
909 ERR("Failed to allocate mem for thread data\n");
910 return STATUS_NO_MEMORY
;
912 LSData
->Flags
= Flags
;
913 LSData
->Session
= Session
;
915 Status
= CreateLogoffSecurityAttributes(&psa
);
916 if (!NT_SUCCESS(Status
))
918 ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status
);
919 HeapFree(GetProcessHeap(), 0, LSData
);
923 /* Run logoff thread */
924 hThread
= CreateThread(psa
, 0, LogoffShutdownThread
, (LPVOID
)LSData
, 0, NULL
);
927 ERR("Unable to create logoff thread, error %lu\n", GetLastError());
928 DestroyLogoffSecurityAttributes(psa
);
929 HeapFree(GetProcessHeap(), 0, LSData
);
930 return STATUS_UNSUCCESSFUL
;
932 WaitForSingleObject(hThread
, INFINITE
);
933 if (!GetExitCodeThread(hThread
, &exitCode
))
935 ERR("Unable to get exit code of logoff thread (error %lu)\n", GetLastError());
936 CloseHandle(hThread
);
937 DestroyLogoffSecurityAttributes(psa
);
938 HeapFree(GetProcessHeap(), 0, LSData
);
939 return STATUS_UNSUCCESSFUL
;
941 CloseHandle(hThread
);
944 ERR("Logoff thread returned failure\n");
945 DestroyLogoffSecurityAttributes(psa
);
946 HeapFree(GetProcessHeap(), 0, LSData
);
947 return STATUS_UNSUCCESSFUL
;
950 SwitchDesktop(Session
->WinlogonDesktop
);
952 // TODO: Play logoff sound!
954 SetWindowStationUser(Session
->InteractiveWindowStation
,
957 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOGGINGOFF);
959 // FIXME: Closing network connections!
960 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_CLOSINGNETWORKCONNECTIONS);
962 /* Kill remaining COM apps. Only at logoff! */
963 hThread
= CreateThread(psa
, 0, KillComProcesses
, (LPVOID
)LSData
, 0, NULL
);
966 WaitForSingleObject(hThread
, INFINITE
);
967 CloseHandle(hThread
);
970 /* We're done with the SECURITY_DESCRIPTOR */
971 DestroyLogoffSecurityAttributes(psa
);
974 HeapFree(GetProcessHeap(), 0, LSData
);
976 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_SAVEYOURSETTINGS
);
978 UnloadUserProfile(Session
->UserToken
, Session
->hProfileInfo
);
980 CallNotificationDlls(Session
, LogoffHandler
);
982 CloseHandle(Session
->UserToken
);
983 UpdatePerUserSystemParameters(0, FALSE
);
984 Session
->LogonState
= STATE_LOGGED_OFF
;
985 Session
->UserToken
= NULL
;
987 return STATUS_SUCCESS
;
993 ShutdownComputerWindowProc(
999 UNREFERENCED_PARAMETER(lParam
);
1005 switch (LOWORD(wParam
))
1007 case IDC_BTNSHTDOWNCOMPUTER
:
1008 EndDialog(hwndDlg
, IDC_BTNSHTDOWNCOMPUTER
);
1015 RemoveMenu(GetSystemMenu(hwndDlg
, FALSE
), SC_CLOSE
, MF_BYCOMMAND
);
1016 SetFocus(GetDlgItem(hwndDlg
, IDC_BTNSHTDOWNCOMPUTER
));
1026 IN OUT PWLSESSION Session
)
1028 if (Session
->SASWindow
)
1030 DestroyWindow(Session
->SASWindow
);
1031 Session
->SASWindow
= NULL
;
1033 if (Session
->hEndOfScreenSaverThread
)
1034 SetEvent(Session
->hEndOfScreenSaverThread
);
1035 UnregisterClassW(WINLOGON_SAS_CLASS
, hAppInstance
);
1040 IN OUT PWLSESSION Session
,
1043 PLOGOFF_SHUTDOWN_DATA LSData
;
1048 // SwitchDesktop(Session->WinlogonDesktop);
1049 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_REACTOSISSHUTTINGDOWN
);
1051 /* Prepare data for shutdown thread */
1052 LSData
= HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA
));
1055 ERR("Failed to allocate mem for thread data\n");
1056 return STATUS_NO_MEMORY
;
1058 if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
)
1059 LSData
->Flags
= EWX_POWEROFF
;
1060 else if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_REBOOT
)
1061 LSData
->Flags
= EWX_REBOOT
;
1063 LSData
->Flags
= EWX_SHUTDOWN
;
1064 LSData
->Session
= Session
;
1066 // FIXME: We may need to specify this flag to really force application kill
1067 // (we are shutting down ReactOS, not just logging off so no hangs, etc...
1068 // should be allowed).
1069 // LSData->Flags |= EWX_FORCE;
1071 /* Run shutdown thread */
1072 hThread
= CreateThread(NULL
, 0, LogoffShutdownThread
, (LPVOID
)LSData
, 0, NULL
);
1075 ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
1076 HeapFree(GetProcessHeap(), 0, LSData
);
1077 return STATUS_UNSUCCESSFUL
;
1079 WaitForSingleObject(hThread
, INFINITE
);
1080 HeapFree(GetProcessHeap(), 0, LSData
);
1081 if (!GetExitCodeThread(hThread
, &exitCode
))
1083 ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError());
1084 CloseHandle(hThread
);
1085 return STATUS_UNSUCCESSFUL
;
1087 CloseHandle(hThread
);
1090 ERR("Shutdown thread returned failure\n");
1091 return STATUS_UNSUCCESSFUL
;
1094 CallNotificationDlls(Session
, ShutdownHandler
);
1096 /* Destroy SAS window */
1097 UninitializeSAS(Session
);
1099 /* Now we can shut down NT */
1100 ERR("Shutting down NT...\n");
1101 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, TRUE
, FALSE
, &Old
);
1102 if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_REBOOT
)
1104 NtShutdownSystem(ShutdownReboot
);
1110 /* FIXME - only show this dialog if it's a shutdown and the computer doesn't support APM */
1111 DialogBox(hAppInstance
, MAKEINTRESOURCE(IDD_SHUTDOWNCOMPUTER
),
1112 GetDesktopWindow(), ShutdownComputerWindowProc
);
1114 NtShutdownSystem(ShutdownNoReboot
);
1116 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE
, Old
, FALSE
, &Old
);
1117 return STATUS_SUCCESS
;
1123 IN OUT PWLSESSION Session
,
1128 case WLX_SAS_ACTION_LOGON
: /* 0x01 */
1129 if (Session
->LogonState
== STATE_LOGGED_OFF_SAS
)
1131 if (!HandleLogon(Session
))
1133 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1134 CallNotificationDlls(Session
, LogonHandler
);
1138 case WLX_SAS_ACTION_NONE
: /* 0x02 */
1139 if (Session
->LogonState
== STATE_LOGGED_OFF_SAS
)
1141 Session
->LogonState
= STATE_LOGGED_OFF
;
1142 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1144 else if (Session
->LogonState
== STATE_LOGGED_ON_SAS
)
1146 Session
->LogonState
= STATE_LOGGED_ON
;
1148 else if (Session
->LogonState
== STATE_LOCKED_SAS
)
1150 Session
->LogonState
= STATE_LOCKED
;
1151 Session
->Gina
.Functions
.WlxDisplayLockedNotice(Session
->Gina
.Context
);
1154 case WLX_SAS_ACTION_LOCK_WKSTA
: /* 0x03 */
1155 if (Session
->Gina
.Functions
.WlxIsLockOk(Session
->Gina
.Context
))
1157 SwitchDesktop(Session
->WinlogonDesktop
);
1158 Session
->LogonState
= STATE_LOCKED
;
1159 Session
->Gina
.Functions
.WlxDisplayLockedNotice(Session
->Gina
.Context
);
1160 CallNotificationDlls(Session
, LockHandler
);
1163 case WLX_SAS_ACTION_LOGOFF
: /* 0x04 */
1164 case WLX_SAS_ACTION_SHUTDOWN
: /* 0x05 */
1165 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
: /* 0x0a */
1166 case WLX_SAS_ACTION_SHUTDOWN_REBOOT
: /* 0x0b */
1167 if (Session
->LogonState
!= STATE_LOGGED_OFF
)
1169 if (!Session
->Gina
.Functions
.WlxIsLogoffOk(Session
->Gina
.Context
))
1171 if (!NT_SUCCESS(HandleLogoff(Session
, EWX_LOGOFF
)))
1173 RemoveStatusMessage(Session
);
1176 Session
->Gina
.Functions
.WlxLogoff(Session
->Gina
.Context
);
1178 if (WLX_SHUTTINGDOWN(wlxAction
))
1180 // FIXME: WlxShutdown should be done from inside HandleShutdown,
1181 // after having displayed "ReactOS is shutting down" message.
1182 Session
->Gina
.Functions
.WlxShutdown(Session
->Gina
.Context
, wlxAction
);
1183 if (!NT_SUCCESS(HandleShutdown(Session
, wlxAction
)))
1185 RemoveStatusMessage(Session
);
1186 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1191 RemoveStatusMessage(Session
);
1192 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1195 case WLX_SAS_ACTION_TASKLIST
: /* 0x07 */
1196 SwitchDesktop(Session
->ApplicationDesktop
);
1197 Session
->LogonState
= STATE_LOGGED_ON
;
1198 StartTaskManager(Session
);
1200 case WLX_SAS_ACTION_UNLOCK_WKSTA
: /* 0x08 */
1201 SwitchDesktop(Session
->ApplicationDesktop
);
1202 Session
->LogonState
= STATE_LOGGED_ON
;
1203 CallNotificationDlls(Session
, UnlockHandler
);
1206 WARN("Unknown SAS action 0x%lx\n", wlxAction
);
1213 IN OUT PWLSESSION Session
,
1216 DWORD wlxAction
= WLX_SAS_ACTION_NONE
;
1217 PSID LogonSid
= NULL
; /* FIXME */
1218 BOOL bSecure
= TRUE
;
1222 case WLX_SAS_TYPE_CTRL_ALT_DEL
:
1223 switch (Session
->LogonState
)
1226 Session
->LogonState
= STATE_LOGGED_OFF
;
1227 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
1230 case STATE_LOGGED_OFF
:
1231 Session
->LogonState
= STATE_LOGGED_OFF_SAS
;
1233 CloseAllDialogWindows();
1235 Session
->Options
= 0;
1237 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxLoggedOutSAS(
1238 Session
->Gina
.Context
,
1243 &Session
->UserToken
,
1244 &Session
->MprNotifyInfo
,
1245 (PVOID
*)&Session
->Profile
);
1248 case STATE_LOGGED_OFF_SAS
:
1249 /* Ignore SAS if we are already in an SAS state */
1252 case STATE_LOGGED_ON
:
1253 Session
->LogonState
= STATE_LOGGED_ON_SAS
;
1254 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxLoggedOnSAS(Session
->Gina
.Context
, dwSasType
, NULL
);
1257 case STATE_LOGGED_ON_SAS
:
1258 /* Ignore SAS if we are already in an SAS state */
1262 Session
->LogonState
= STATE_LOCKED_SAS
;
1264 CloseAllDialogWindows();
1266 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxWkstaLockedSAS(Session
->Gina
.Context
, dwSasType
);
1269 case STATE_LOCKED_SAS
:
1270 /* Ignore SAS if we are already in an SAS state */
1278 case WLX_SAS_TYPE_TIMEOUT
:
1281 case WLX_SAS_TYPE_SCRNSVR_TIMEOUT
:
1282 if (!Session
->Gina
.Functions
.WlxScreenSaverNotify(Session
->Gina
.Context
, &bSecure
))
1284 /* Skip start of screen saver */
1285 SetEvent(Session
->hEndOfScreenSaver
);
1289 StartScreenSaver(Session
);
1292 wlxAction
= WLX_SAS_ACTION_LOCK_WKSTA
;
1293 // DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
1298 case WLX_SAS_TYPE_SCRNSVR_ACTIVITY
:
1299 SetEvent(Session
->hUserActivity
);
1303 DoGenericAction(Session
, wlxAction
);
1309 IN PWLSESSION Session
,
1312 /* Register Ctrl+Alt+Del Hotkey */
1313 if (!RegisterHotKey(hwndSAS
, HK_CTRL_ALT_DEL
, MOD_CONTROL
| MOD_ALT
, VK_DELETE
))
1315 ERR("WL: Unable to register Ctrl+Alt+Del hotkey!\n");
1319 /* Register Ctrl+Shift+Esc (optional) */
1320 Session
->TaskManHotkey
= RegisterHotKey(hwndSAS
, HK_CTRL_SHIFT_ESC
, MOD_CONTROL
| MOD_SHIFT
, VK_ESCAPE
);
1321 if (!Session
->TaskManHotkey
)
1322 WARN("WL: Warning: Unable to register Ctrl+Alt+Esc hotkey!\n");
1329 IN PWLSESSION Session
,
1332 /* Unregister hotkeys */
1333 UnregisterHotKey(hwndSAS
, HK_CTRL_ALT_DEL
);
1335 if (Session
->TaskManHotkey
)
1336 UnregisterHotKey(hwndSAS
, HK_CTRL_SHIFT_ESC
);
1343 HandleMessageBeep(UINT uType
)
1353 EventName
= L
"SystemDefault";
1355 case MB_ICONASTERISK
:
1356 EventName
= L
"SystemAsterisk";
1358 case MB_ICONEXCLAMATION
:
1359 EventName
= L
"SystemExclamation";
1362 EventName
= L
"SystemHand";
1364 case MB_ICONQUESTION
:
1365 EventName
= L
"SystemQuestion";
1368 WARN("Unhandled type %d\n", uType
);
1369 EventName
= L
"SystemDefault";
1372 return PlaySoundRoutine(EventName
, FALSE
, SND_ALIAS
| SND_NOWAIT
| SND_NOSTOP
| SND_ASYNC
);
1384 PWLSESSION Session
= (PWLSESSION
)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1392 case MAKELONG(MOD_CONTROL
| MOD_ALT
, VK_DELETE
):
1394 TRACE("SAS: CONTROL+ALT+DELETE\n");
1395 if (!Session
->Gina
.UseCtrlAltDelete
)
1397 PostMessageW(Session
->SASWindow
, WLX_WM_SAS
, WLX_SAS_TYPE_CTRL_ALT_DEL
, 0);
1400 case MAKELONG(MOD_CONTROL
| MOD_SHIFT
, VK_ESCAPE
):
1402 TRACE("SAS: CONTROL+SHIFT+ESCAPE\n");
1403 if (Session
->LogonState
== STATE_LOGGED_ON
)
1404 DoGenericAction(Session
, WLX_SAS_ACTION_TASKLIST
);
1412 /* Get the session pointer from the create data */
1413 Session
= (PWLSESSION
)((LPCREATESTRUCT
)lParam
)->lpCreateParams
;
1415 /* Save the Session pointer */
1416 SetWindowLongPtrW(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)Session
);
1419 return RegisterHotKeys(Session
, hwndDlg
);
1423 if (!GetSetupType())
1424 UnregisterHotKeys(Session
, hwndDlg
);
1427 case WM_SETTINGCHANGE
:
1429 UINT uiAction
= (UINT
)wParam
;
1430 if (uiAction
== SPI_SETSCREENSAVETIMEOUT
1431 || uiAction
== SPI_SETSCREENSAVEACTIVE
)
1433 SetEvent(Session
->hScreenSaverParametersChanged
);
1437 case WM_LOGONNOTIFY
:
1441 case LN_MESSAGE_BEEP
:
1443 return HandleMessageBeep(lParam
);
1445 case LN_SHELL_EXITED
:
1447 /* lParam is the exit code */
1449 Session
->LogonState
!= STATE_LOGGED_OFF
&&
1450 Session
->LogonState
!= STATE_LOGGED_OFF_SAS
)
1452 SetTimer(hwndDlg
, 1, 1000, NULL
);
1456 case LN_START_SCREENSAVE
:
1458 DispatchSAS(Session
, WLX_SAS_TYPE_SCRNSVR_TIMEOUT
);
1461 case LN_LOCK_WORKSTATION
:
1463 DoGenericAction(Session
, WLX_SAS_ACTION_LOCK_WKSTA
);
1468 UINT Flags
= (UINT
)lParam
;
1469 UINT Action
= Flags
& EWX_ACTION_MASK
;
1472 TRACE("\tFlags : 0x%lx\n", lParam
);
1475 * Our caller (USERSRV) should have added the shutdown flag
1476 * when setting also poweroff or reboot.
1478 if (Action
& (EWX_POWEROFF
| EWX_REBOOT
))
1480 if ((Action
& EWX_SHUTDOWN
) == 0)
1482 ERR("Missing EWX_SHUTDOWN flag for poweroff or reboot; action 0x%x\n", Action
);
1483 return STATUS_INVALID_PARAMETER
;
1486 /* Now we can locally remove it for performing checks */
1487 Action
&= ~EWX_SHUTDOWN
;
1490 /* Check parameters */
1491 if (Action
& EWX_FORCE
)
1494 ERR("FIXME: EWX_FORCE present for Winlogon, what to do?\n");
1495 Action
&= ~EWX_FORCE
;
1500 wlxAction
= WLX_SAS_ACTION_LOGOFF
;
1503 wlxAction
= WLX_SAS_ACTION_SHUTDOWN
;
1506 wlxAction
= WLX_SAS_ACTION_SHUTDOWN_REBOOT
;
1509 wlxAction
= WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
;
1514 ERR("Invalid ExitWindows action 0x%x\n", Action
);
1515 return STATUS_INVALID_PARAMETER
;
1519 TRACE("In LN_LOGOFF, exit_in_progress == %s\n",
1520 ExitReactOSInProgress
? "true" : "false");
1523 * In case a parallel shutdown request is done (while we are
1524 * being to shut down) and it was not done by Winlogon itself,
1525 * then just stop here.
1528 // This code is commented at the moment (even if it's correct) because
1529 // our log-offs do not really work: the shell is restarted, no app is killed
1530 // etc... and as a result you just get explorer opening "My Documents". And
1531 // if you try now a shut down, it won't work because winlogon thinks it is
1532 // still in the middle of a shutdown.
1533 // Maybe we also need to reset ExitReactOSInProgress somewhere else??
1534 if (ExitReactOSInProgress
&& (lParam
& EWX_CALLER_WINLOGON
) == 0)
1539 /* Now do the shutdown action proper */
1540 DoGenericAction(Session
, wlxAction
);
1543 case LN_LOGOFF_CANCELED
:
1545 ERR("Logoff canceled!!, before: exit_in_progress == %s, after will be false\n",
1546 ExitReactOSInProgress
? "true" : "false");
1548 ExitReactOSInProgress
= FALSE
;
1553 ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam
);
1562 KillTimer(hwndDlg
, 1);
1563 StartUserShell(Session
);
1569 DispatchSAS(Session
, (DWORD
)wParam
);
1574 return DefWindowProc(hwndDlg
, uMsg
, wParam
, lParam
);
1579 IN OUT PWLSESSION Session
)
1584 if (!SwitchDesktop(Session
->WinlogonDesktop
))
1586 ERR("WL: Failed to switch to winlogon desktop\n");
1590 /* Register SAS window class */
1591 swc
.cbSize
= sizeof(WNDCLASSEXW
);
1592 swc
.style
= CS_SAVEBITS
;
1593 swc
.lpfnWndProc
= SASWindowProc
;
1596 swc
.hInstance
= hAppInstance
;
1599 swc
.hbrBackground
= NULL
;
1600 swc
.lpszMenuName
= NULL
;
1601 swc
.lpszClassName
= WINLOGON_SAS_CLASS
;
1603 if (RegisterClassExW(&swc
) == 0)
1605 ERR("WL: Failed to register SAS window class\n");
1609 /* Create invisible SAS window */
1610 Session
->SASWindow
= CreateWindowExW(
1616 hAppInstance
, Session
);
1617 if (!Session
->SASWindow
)
1619 ERR("WL: Failed to create SAS window\n");
1623 /* Register SAS window to receive SAS notifications */
1624 if (!SetLogonNotifyWindow(Session
->SASWindow
))
1626 ERR("WL: Failed to register SAS window\n");
1630 if (!SetDefaultLanguage(NULL
))
1637 UninitializeSAS(Session
);