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>
22 #include <reactos/winlogon.h>
24 /* GLOBALS ******************************************************************/
26 #define WINLOGON_SAS_CLASS L"SAS Window class"
27 #define WINLOGON_SAS_TITLE L"SAS window"
29 #define HK_CTRL_ALT_DEL 0
30 #define HK_CTRL_SHIFT_ESC 1
32 #define EWX_ACTION_MASK 0xffffffeb
33 #define EWX_FLAGS_MASK 0x00000014
35 typedef struct tagLOGOFF_SHUTDOWN_DATA
39 } LOGOFF_SHUTDOWN_DATA
, *PLOGOFF_SHUTDOWN_DATA
;
41 /* FUNCTIONS ****************************************************************/
45 IN OUT PWLSESSION Session
)
50 if (!Session
->Gina
.Functions
.WlxStartApplication
)
53 if (!CreateEnvironmentBlock(
61 ret
= Session
->Gina
.Functions
.WlxStartApplication(
62 Session
->Gina
.Context
,
67 DestroyEnvironmentBlock(lpEnvironment
);
73 IN OUT PWLSESSION Session
)
75 LPVOID lpEnvironment
= NULL
;
79 /* Create environment block for the user */
80 if (!CreateEnvironmentBlock(&lpEnvironment
, Session
->UserToken
, TRUE
))
82 WARN("WL: CreateEnvironmentBlock() failed\n");
87 /* FIXME: who should do it? winlogon or gina? */
88 /* FIXME: reverting to lower privileges after creating user shell? */
89 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE
, TRUE
, FALSE
, &Old
);
91 ret
= Session
->Gina
.Functions
.WlxActivateUserShell(
92 Session
->Gina
.Context
,
97 DestroyEnvironmentBlock(lpEnvironment
);
111 DWORD dwType
, dwSize
;
113 UNICODE_STRING ValueString
;
120 BaseKey
= HKEY_CURRENT_USER
;
121 SubKey
= L
"Control Panel\\International";
122 ValueName
= L
"Locale";
126 BaseKey
= HKEY_LOCAL_MACHINE
;
127 SubKey
= L
"System\\CurrentControlSet\\Control\\Nls\\Language";
128 ValueName
= L
"Default";
137 if (rc
!= ERROR_SUCCESS
)
139 TRACE("RegOpenKeyEx() failed with error %lu\n", rc
);
142 rc
= RegQueryValueExW(
149 if (rc
!= ERROR_SUCCESS
)
151 TRACE("RegQueryValueEx() failed with error %lu\n", rc
);
154 else if (dwType
!= REG_SZ
)
156 TRACE("Wrong type for %S\\%S registry entry (got 0x%lx, expected 0x%x)\n",
157 SubKey
, ValueName
, dwType
, REG_SZ
);
161 Value
= HeapAlloc(GetProcessHeap(), 0, dwSize
);
164 TRACE("HeapAlloc() failed\n");
167 rc
= RegQueryValueExW(
174 if (rc
!= ERROR_SUCCESS
)
176 TRACE("RegQueryValueEx() failed with error %lu\n", rc
);
180 /* Convert Value to a Lcid */
181 ValueString
.Length
= ValueString
.MaximumLength
= (USHORT
)dwSize
;
182 ValueString
.Buffer
= Value
;
183 Status
= RtlUnicodeStringToInteger(&ValueString
, 16, (PULONG
)&Lcid
);
184 if (!NT_SUCCESS(Status
))
186 TRACE("RtlUnicodeStringToInteger() failed with status 0x%08lx\n", Status
);
190 TRACE("%s language is 0x%08lx\n",
191 UserProfile
? "User" : "System", Lcid
);
192 Status
= NtSetDefaultLocale(UserProfile
, Lcid
);
193 if (!NT_SUCCESS(Status
))
195 TRACE("NtSetDefaultLocale() failed with status 0x%08lx\n", Status
);
205 HeapFree(GetProcessHeap(), 0, Value
);
215 typedef BOOL (WINAPI
*PLAYSOUNDW
)(LPCWSTR
,HMODULE
,DWORD
);
216 typedef UINT (WINAPI
*WAVEOUTGETNUMDEVS
)(VOID
);
218 WAVEOUTGETNUMDEVS waveOutGetNumDevs
;
223 hLibrary
= LoadLibraryW(L
"winmm.dll");
226 waveOutGetNumDevs
= (WAVEOUTGETNUMDEVS
)GetProcAddress(hLibrary
, "waveOutGetNumDevs");
227 if (waveOutGetNumDevs
)
229 NumDevs
= waveOutGetNumDevs();
236 FreeLibrary(hLibrary
);
241 Play
= (PLAYSOUNDW
)GetProcAddress(hLibrary
, "PlaySoundW");
244 Ret
= Play(FileName
, NULL
, Flags
);
246 FreeLibrary(hLibrary
);
254 PlayLogonSoundThread(
255 IN LPVOID lpParameter
)
257 BYTE TokenUserBuffer
[256];
258 PTOKEN_USER pTokenUser
= (TOKEN_USER
*)TokenUserBuffer
;
261 WCHAR wszBuffer
[MAX_PATH
] = {0};
262 WCHAR wszDest
[MAX_PATH
];
263 DWORD dwSize
= sizeof(wszBuffer
), dwType
;
264 SERVICE_STATUS_PROCESS Info
;
265 UNICODE_STRING SidString
;
268 SC_HANDLE hSCManager
, hService
;
270 /* Get SID of current user */
271 Status
= NtQueryInformationToken((HANDLE
)lpParameter
,
274 sizeof(TokenUserBuffer
),
276 if (!NT_SUCCESS(Status
))
278 ERR("NtQueryInformationToken failed: %x!\n", Status
);
282 /* Convert SID to string */
283 RtlInitEmptyUnicodeString(&SidString
, wszBuffer
, sizeof(wszBuffer
));
284 Status
= RtlConvertSidToUnicodeString(&SidString
, pTokenUser
->User
.Sid
, FALSE
);
285 if (!NT_SUCCESS(Status
))
287 ERR("RtlConvertSidToUnicodeString failed: %x!\n", Status
);
291 /* Build path to logon sound registry key.
292 Note: We can't use HKCU here, because Winlogon is owned by SYSTEM user */
293 if (FAILED(StringCbCopyW(wszBuffer
+ SidString
.Length
/sizeof(WCHAR
),
294 sizeof(wszBuffer
) - SidString
.Length
,
295 L
"\\AppEvents\\Schemes\\Apps\\.Default\\WindowsLogon\\.Current")))
297 /* SID is too long. Should not happen. */
298 ERR("StringCbCopyW failed!\n");
302 /* Open registry key and query sound path */
303 if (RegOpenKeyExW(HKEY_USERS
, wszBuffer
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
305 ERR("RegOpenKeyExW(%ls) failed!\n", wszBuffer
);
309 if (RegQueryValueExW(hKey
, NULL
, NULL
, &dwType
,
310 (LPBYTE
)wszBuffer
, &dwSize
) != ERROR_SUCCESS
||
311 (dwType
!= REG_SZ
&& dwType
!= REG_EXPAND_SZ
))
313 ERR("RegQueryValueExW failed!\n");
322 /* No sound has been set */
323 ERR("No sound has been set\n");
327 /* Expand environment variables */
328 if (!ExpandEnvironmentStringsW(wszBuffer
, wszDest
, MAX_PATH
))
330 ERR("ExpandEnvironmentStringsW failed!\n");
334 /* Open service manager */
335 hSCManager
= OpenSCManager(NULL
, NULL
, SC_MANAGER_CONNECT
);
338 ERR("OpenSCManager failed (%x)\n", GetLastError());
342 /* Open wdmaud service */
343 hService
= OpenServiceW(hSCManager
, L
"wdmaud", GENERIC_READ
);
346 /* Sound is not installed */
347 TRACE("Failed to open wdmaud service (%x)\n", GetLastError());
348 CloseServiceHandle(hSCManager
);
352 /* Wait for wdmaud start */
355 if (!QueryServiceStatusEx(hService
, SC_STATUS_PROCESS_INFO
, (LPBYTE
)&Info
, sizeof(SERVICE_STATUS_PROCESS
), &dwSize
))
357 TRACE("QueryServiceStatusEx failed (%x)\n", GetLastError());
361 if (Info
.dwCurrentState
== SERVICE_RUNNING
)
366 } while (Index
++ < 20);
368 CloseServiceHandle(hService
);
369 CloseServiceHandle(hSCManager
);
371 /* If wdmaud is not running exit */
372 if (Info
.dwCurrentState
!= SERVICE_RUNNING
)
374 WARN("wdmaud has not started!\n");
378 /* Sound subsystem is running. Play logon sound. */
379 TRACE("Playing logon sound: %ls\n", wszDest
);
380 PlaySoundRoutine(wszDest
, TRUE
, SND_FILENAME
);
387 IN OUT PWLSESSION Session
)
391 hThread
= CreateThread(NULL
, 0, PlayLogonSoundThread
, (PVOID
)Session
->UserToken
, 0, NULL
);
393 CloseHandle(hThread
);
399 IN OUT PWLSESSION Session
)
401 PROFILEINFOW ProfileInfo
;
404 /* Loading personal settings */
405 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_LOADINGYOURPERSONALSETTINGS
);
406 ProfileInfo
.hProfile
= INVALID_HANDLE_VALUE
;
407 if (0 == (Session
->Options
& WLX_LOGON_OPT_NO_PROFILE
))
409 if (Session
->Profile
== NULL
410 || (Session
->Profile
->dwType
!= WLX_PROFILE_TYPE_V1_0
411 && Session
->Profile
->dwType
!= WLX_PROFILE_TYPE_V2_0
))
413 ERR("WL: Wrong profile\n");
417 /* Load the user profile */
418 ZeroMemory(&ProfileInfo
, sizeof(PROFILEINFOW
));
419 ProfileInfo
.dwSize
= sizeof(PROFILEINFOW
);
420 ProfileInfo
.dwFlags
= 0;
421 ProfileInfo
.lpUserName
= Session
->MprNotifyInfo
.pszUserName
;
422 ProfileInfo
.lpProfilePath
= Session
->Profile
->pszProfile
;
423 if (Session
->Profile
->dwType
>= WLX_PROFILE_TYPE_V2_0
)
425 ProfileInfo
.lpDefaultPath
= Session
->Profile
->pszNetworkDefaultUserProfile
;
426 ProfileInfo
.lpServerName
= Session
->Profile
->pszServerName
;
427 ProfileInfo
.lpPolicyPath
= Session
->Profile
->pszPolicy
;
430 if (!LoadUserProfileW(Session
->UserToken
, &ProfileInfo
))
432 ERR("WL: LoadUserProfileW() failed\n");
437 /* Create environment block for the user */
438 if (!CreateUserEnvironment(Session
))
440 WARN("WL: SetUserEnvironment() failed\n");
444 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_APPLYINGYOURPERSONALSETTINGS
);
445 UpdatePerUserSystemParameters(0, TRUE
);
447 /* Set default language */
448 if (!SetDefaultLanguage(TRUE
))
450 WARN("WL: SetDefaultLanguage() failed\n");
454 if (!StartUserShell(Session
))
456 //WCHAR StatusMsg[256];
457 WARN("WL: WlxActivateUserShell() failed\n");
458 //LoadStringW(hAppInstance, IDS_FAILEDACTIVATEUSERSHELL, StatusMsg, sizeof(StatusMsg) / sizeof(StatusMsg[0]));
459 //MessageBoxW(0, StatusMsg, NULL, MB_ICONERROR);
463 if (!InitializeScreenSaver(Session
))
464 WARN("WL: Failed to initialize screen saver\n");
466 Session
->hProfileInfo
= ProfileInfo
.hProfile
;
468 /* Logon has successed. Play sound. */
469 PlayLogonSound(Session
);
474 if (Session
->Profile
)
476 HeapFree(GetProcessHeap(), 0, Session
->Profile
->pszProfile
);
477 HeapFree(GetProcessHeap(), 0, Session
->Profile
);
479 Session
->Profile
= NULL
;
481 && ProfileInfo
.hProfile
!= INVALID_HANDLE_VALUE
)
483 UnloadUserProfile(WLSession
->UserToken
, ProfileInfo
.hProfile
);
485 RemoveStatusMessage(Session
);
488 CloseHandle(Session
->UserToken
);
489 Session
->UserToken
= NULL
;
498 LogoffShutdownThread(
501 PLOGOFF_SHUTDOWN_DATA LSData
= (PLOGOFF_SHUTDOWN_DATA
)Parameter
;
503 if (LSData
->Session
->UserToken
!= NULL
&& !ImpersonateLoggedOnUser(LSData
->Session
->UserToken
))
505 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
509 /* Close processes of the interactive user */
511 EWX_INTERNAL_KILL_USER_APPS
| (LSData
->Flags
& EWX_FLAGS_MASK
) |
512 (EWX_LOGOFF
== (LSData
->Flags
& EWX_ACTION_MASK
) ? EWX_INTERNAL_FLAG_LOGOFF
: 0),
515 ERR("Unable to kill user apps, error %lu\n", GetLastError());
520 /* FIXME: Call ExitWindowsEx() to terminate COM processes */
522 if (LSData
->Session
->UserToken
)
531 CreateLogoffSecurityAttributes(
532 OUT PSECURITY_ATTRIBUTES
* ppsa
)
534 /* The following code is not working yet and messy */
535 /* Still, it gives some ideas about data types and functions involved and */
536 /* required to set up a SECURITY_DESCRIPTOR for a SECURITY_ATTRIBUTES */
537 /* instance for a thread, to allow that thread to ImpersonateLoggedOnUser(). */
538 /* Specifically THREAD_SET_THREAD_TOKEN is required. */
539 PSECURITY_DESCRIPTOR SecurityDescriptor
= NULL
;
540 PSECURITY_ATTRIBUTES psa
= 0;
543 EXPLICIT_ACCESS Access
;
544 PSID pEveryoneSID
= NULL
;
545 static SID_IDENTIFIER_AUTHORITY WorldAuthority
= { SECURITY_WORLD_SID_AUTHORITY
};
549 // Let's first try to enumerate what kind of data we need for this to ever work:
550 // 1. The Winlogon SID, to be able to give it THREAD_SET_THREAD_TOKEN.
551 // 2. The users SID (the user trying to logoff, or rather shut down the system).
552 // 3. At least two EXPLICIT_ACCESS instances:
553 // 3.1 One for Winlogon itself, giving it the rights
554 // required to THREAD_SET_THREAD_TOKEN (as it's needed to successfully call
555 // ImpersonateLoggedOnUser).
556 // 3.2 One for the user, to allow *that* thread to perform its work.
557 // 4. An ACL to hold the these EXPLICIT_ACCESS ACE's.
558 // 5. A SECURITY_DESCRIPTOR to hold the ACL, and finally.
559 // 6. A SECURITY_ATTRIBUTES instance to pull all of this required stuff
560 // together, to hand it to CreateThread.
562 // However, it seems struct LOGOFF_SHUTDOWN_DATA doesn't contain
563 // these required SID's, why they'd have to be added.
564 // The Winlogon's own SID should probably only be created once,
565 // while the user's SID obviously must be created for each new user.
566 // Might as well store it when the user logs on?
568 if(!AllocateAndInitializeSid(&WorldAuthority
,
574 ERR("Failed to initialize security descriptor for logoff thread!\n");
575 return STATUS_UNSUCCESSFUL
;
578 /* set up the required security attributes to be able to shut down */
579 /* To save space and time, allocate a single block of memory holding */
580 /* both SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR */
581 pMem
= HeapAlloc(GetProcessHeap(),
583 sizeof(SECURITY_ATTRIBUTES
) +
584 SECURITY_DESCRIPTOR_MIN_LENGTH
+
588 ERR("Failed to allocate memory for logoff security descriptor!\n");
589 return STATUS_NO_MEMORY
;
592 /* Note that the security descriptor needs to be in _absolute_ format, */
593 /* meaning its members must be pointers to other structures, rather */
594 /* than the relative format using offsets */
595 psa
= (PSECURITY_ATTRIBUTES
)pMem
;
596 SecurityDescriptor
= (PSECURITY_DESCRIPTOR
)(pMem
+ sizeof(SECURITY_ATTRIBUTES
));
597 pACL
= (PACL
)(((PBYTE
)SecurityDescriptor
) + SECURITY_DESCRIPTOR_MIN_LENGTH
);
599 // Initialize an EXPLICIT_ACCESS structure for an ACE.
600 // The ACE will allow this thread to log off (and shut down the system, currently).
601 ZeroMemory(&Access
, sizeof(Access
));
602 Access
.grfAccessPermissions
= THREAD_SET_THREAD_TOKEN
;
603 Access
.grfAccessMode
= SET_ACCESS
; // GRANT_ACCESS?
604 Access
.grfInheritance
= NO_INHERITANCE
;
605 Access
.Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
606 Access
.Trustee
.TrusteeType
= TRUSTEE_IS_WELL_KNOWN_GROUP
;
607 Access
.Trustee
.ptstrName
= pEveryoneSID
;
609 if (SetEntriesInAcl(1, &Access
, NULL
, &pACL
) != ERROR_SUCCESS
)
611 ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n");
613 HeapFree(GetProcessHeap(), 0, pMem
);
614 return STATUS_UNSUCCESSFUL
;
617 if (!InitializeSecurityDescriptor(SecurityDescriptor
, SECURITY_DESCRIPTOR_REVISION
))
619 ERR("Failed to initialize security descriptor for logoff thread!\n");
620 HeapFree(GetProcessHeap(), 0, pMem
);
621 return STATUS_UNSUCCESSFUL
;
624 if (!SetSecurityDescriptorDacl(SecurityDescriptor
,
625 TRUE
, // bDaclPresent flag
627 FALSE
)) // not a default DACL
629 ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
630 HeapFree(GetProcessHeap(), 0, pMem
);
631 return STATUS_UNSUCCESSFUL
;
634 psa
->nLength
= sizeof(SECURITY_ATTRIBUTES
);
635 psa
->lpSecurityDescriptor
= SecurityDescriptor
;
636 psa
->bInheritHandle
= FALSE
;
640 return STATUS_SUCCESS
;
645 DestroyLogoffSecurityAttributes(
646 IN PSECURITY_ATTRIBUTES psa
)
650 HeapFree(GetProcessHeap(), 0, psa
);
658 IN OUT PWLSESSION Session
,
661 PLOGOFF_SHUTDOWN_DATA LSData
;
662 PSECURITY_ATTRIBUTES psa
;
667 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_SAVEYOURSETTINGS
);
669 /* Prepare data for logoff thread */
670 LSData
= HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA
));
673 ERR("Failed to allocate mem for thread data\n");
674 return STATUS_NO_MEMORY
;
676 LSData
->Flags
= Flags
;
677 LSData
->Session
= Session
;
679 Status
= CreateLogoffSecurityAttributes(&psa
);
680 if (!NT_SUCCESS(Status
))
682 ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status
);
683 HeapFree(GetProcessHeap(), 0, LSData
);
687 /* Run logoff thread */
688 hThread
= CreateThread(psa
, 0, LogoffShutdownThread
, (LPVOID
)LSData
, 0, NULL
);
690 /* we're done with the SECURITY_DESCRIPTOR */
691 DestroyLogoffSecurityAttributes(psa
);
696 ERR("Unable to create logoff thread, error %lu\n", GetLastError());
697 HeapFree(GetProcessHeap(), 0, LSData
);
698 return STATUS_UNSUCCESSFUL
;
700 WaitForSingleObject(hThread
, INFINITE
);
701 HeapFree(GetProcessHeap(), 0, LSData
);
702 if (!GetExitCodeThread(hThread
, &exitCode
))
704 ERR("Unable to get exit code of logoff thread (error %lu)\n", GetLastError());
705 CloseHandle(hThread
);
706 return STATUS_UNSUCCESSFUL
;
708 CloseHandle(hThread
);
711 ERR("Logoff thread returned failure\n");
712 return STATUS_UNSUCCESSFUL
;
715 UnloadUserProfile(Session
->UserToken
, Session
->hProfileInfo
);
716 CloseHandle(Session
->UserToken
);
717 UpdatePerUserSystemParameters(0, FALSE
);
718 Session
->LogonState
= STATE_LOGGED_OFF
;
719 Session
->UserToken
= NULL
;
720 return STATUS_SUCCESS
;
726 ShutdownComputerWindowProc(
732 UNREFERENCED_PARAMETER(lParam
);
738 switch (LOWORD(wParam
))
740 case IDC_BTNSHTDOWNCOMPUTER
:
741 EndDialog(hwndDlg
, IDC_BTNSHTDOWNCOMPUTER
);
748 RemoveMenu(GetSystemMenu(hwndDlg
, FALSE
), SC_CLOSE
, MF_BYCOMMAND
);
749 SetFocus(GetDlgItem(hwndDlg
, IDC_BTNSHTDOWNCOMPUTER
));
759 IN OUT PWLSESSION Session
)
761 if (Session
->SASWindow
)
763 DestroyWindow(Session
->SASWindow
);
764 Session
->SASWindow
= NULL
;
766 if (Session
->hEndOfScreenSaverThread
)
767 SetEvent(Session
->hEndOfScreenSaverThread
);
768 UnregisterClassW(WINLOGON_SAS_CLASS
, hAppInstance
);
773 IN OUT PWLSESSION Session
,
776 PLOGOFF_SHUTDOWN_DATA LSData
;
780 DisplayStatusMessage(Session
, Session
->WinlogonDesktop
, IDS_REACTOSISSHUTTINGDOWN
);
782 /* Prepare data for shutdown thread */
783 LSData
= HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA
));
786 ERR("Failed to allocate mem for thread data\n");
787 return STATUS_NO_MEMORY
;
789 if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
)
790 LSData
->Flags
= EWX_POWEROFF
;
791 else if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_REBOOT
)
792 LSData
->Flags
= EWX_REBOOT
;
794 LSData
->Flags
= EWX_SHUTDOWN
;
795 LSData
->Session
= Session
;
797 /* Run shutdown thread */
798 hThread
= CreateThread(NULL
, 0, LogoffShutdownThread
, (LPVOID
)LSData
, 0, NULL
);
801 ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
802 HeapFree(GetProcessHeap(), 0, LSData
);
803 return STATUS_UNSUCCESSFUL
;
805 WaitForSingleObject(hThread
, INFINITE
);
806 HeapFree(GetProcessHeap(), 0, LSData
);
807 if (!GetExitCodeThread(hThread
, &exitCode
))
809 ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError());
810 CloseHandle(hThread
);
811 return STATUS_UNSUCCESSFUL
;
813 CloseHandle(hThread
);
816 ERR("Shutdown thread returned failure\n");
817 return STATUS_UNSUCCESSFUL
;
820 /* Destroy SAS window */
821 UninitializeSAS(Session
);
823 FIXME("FIXME: Call SMSS API #1\n");
824 if (wlxAction
== WLX_SAS_ACTION_SHUTDOWN_REBOOT
)
825 NtShutdownSystem(ShutdownReboot
);
830 /* FIXME - only show this dialog if it's a shutdown and the computer doesn't support APM */
831 DialogBox(hAppInstance
, MAKEINTRESOURCE(IDD_SHUTDOWNCOMPUTER
), GetDesktopWindow(), ShutdownComputerWindowProc
);
833 NtShutdownSystem(ShutdownNoReboot
);
835 return STATUS_SUCCESS
;
841 IN OUT PWLSESSION Session
,
846 case WLX_SAS_ACTION_LOGON
: /* 0x01 */
847 if (Session
->LogonState
== STATE_LOGGED_OFF_SAS
)
849 if (HandleLogon(Session
))
851 SwitchDesktop(Session
->ApplicationDesktop
);
852 Session
->LogonState
= STATE_LOGGED_ON
;
856 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
860 case WLX_SAS_ACTION_NONE
: /* 0x02 */
861 if (Session
->LogonState
== STATE_LOGGED_OFF_SAS
)
863 Session
->LogonState
= STATE_LOGGED_OFF
;
864 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
866 else if (Session
->LogonState
== STATE_LOGGED_ON_SAS
)
868 Session
->LogonState
= STATE_LOGGED_ON
;
870 else if (Session
->LogonState
== STATE_LOCKED_SAS
)
872 Session
->LogonState
= STATE_LOCKED
;
873 Session
->Gina
.Functions
.WlxDisplayLockedNotice(Session
->Gina
.Context
);
876 case WLX_SAS_ACTION_LOCK_WKSTA
: /* 0x03 */
877 if (Session
->Gina
.Functions
.WlxIsLockOk(Session
->Gina
.Context
))
879 SwitchDesktop(WLSession
->WinlogonDesktop
);
880 Session
->LogonState
= STATE_LOCKED
;
881 Session
->Gina
.Functions
.WlxDisplayLockedNotice(Session
->Gina
.Context
);
884 case WLX_SAS_ACTION_LOGOFF
: /* 0x04 */
885 case WLX_SAS_ACTION_SHUTDOWN
: /* 0x05 */
886 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
: /* 0x0a */
887 case WLX_SAS_ACTION_SHUTDOWN_REBOOT
: /* 0x0b */
888 if (Session
->LogonState
!= STATE_LOGGED_OFF
)
890 if (!Session
->Gina
.Functions
.WlxIsLogoffOk(Session
->Gina
.Context
))
892 SwitchDesktop(WLSession
->WinlogonDesktop
);
893 Session
->Gina
.Functions
.WlxLogoff(Session
->Gina
.Context
);
894 if (!NT_SUCCESS(HandleLogoff(Session
, EWX_LOGOFF
)))
896 RemoveStatusMessage(Session
);
900 if (WLX_SHUTTINGDOWN(wlxAction
))
902 Session
->Gina
.Functions
.WlxShutdown(Session
->Gina
.Context
, wlxAction
);
903 if (!NT_SUCCESS(HandleShutdown(Session
, wlxAction
)))
905 RemoveStatusMessage(Session
);
906 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
911 RemoveStatusMessage(Session
);
912 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
915 case WLX_SAS_ACTION_TASKLIST
: /* 0x07 */
916 SwitchDesktop(WLSession
->ApplicationDesktop
);
917 Session
->LogonState
= STATE_LOGGED_ON
;
918 StartTaskManager(Session
);
920 case WLX_SAS_ACTION_UNLOCK_WKSTA
: /* 0x08 */
921 SwitchDesktop(WLSession
->ApplicationDesktop
);
922 Session
->LogonState
= STATE_LOGGED_ON
;
925 WARN("Unknown SAS action 0x%lx\n", wlxAction
);
932 IN OUT PWLSESSION Session
,
935 DWORD wlxAction
= WLX_SAS_ACTION_NONE
;
937 PSID LogonSid
= NULL
; /* FIXME */
942 case WLX_SAS_TYPE_CTRL_ALT_DEL
:
943 switch (Session
->LogonState
)
946 Session
->LogonState
= STATE_LOGGED_OFF
;
947 Session
->Gina
.Functions
.WlxDisplaySASNotice(Session
->Gina
.Context
);
950 case STATE_LOGGED_OFF
:
951 Session
->LogonState
= STATE_LOGGED_OFF_SAS
;
953 hwnd
= GetTopDialogWindow();
955 SendMessage(hwnd
, WLX_WM_SAS
, 0, 0);
957 Session
->Options
= 0;
959 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxLoggedOutSAS(
960 Session
->Gina
.Context
,
966 &Session
->MprNotifyInfo
,
967 (PVOID
*)&Session
->Profile
);
970 case STATE_LOGGED_OFF_SAS
:
971 /* Ignore SAS if we are already in an SAS state */
974 case STATE_LOGGED_ON
:
975 Session
->LogonState
= STATE_LOGGED_ON_SAS
;
976 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxLoggedOnSAS(Session
->Gina
.Context
, dwSasType
, NULL
);
979 case STATE_LOGGED_ON_SAS
:
980 /* Ignore SAS if we are already in an SAS state */
984 Session
->LogonState
= STATE_LOCKED_SAS
;
986 hwnd
= GetTopDialogWindow();
988 SendMessage(hwnd
, WLX_WM_SAS
, 0, 0);
990 wlxAction
= (DWORD
)Session
->Gina
.Functions
.WlxWkstaLockedSAS(Session
->Gina
.Context
, dwSasType
);
993 case STATE_LOCKED_SAS
:
994 /* Ignore SAS if we are already in an SAS state */
1002 case WLX_SAS_TYPE_TIMEOUT
:
1005 case WLX_SAS_TYPE_SCRNSVR_TIMEOUT
:
1006 if (!Session
->Gina
.Functions
.WlxScreenSaverNotify(Session
->Gina
.Context
, &bSecure
))
1008 /* Skip start of screen saver */
1009 SetEvent(Session
->hEndOfScreenSaver
);
1013 StartScreenSaver(Session
);
1016 wlxAction
= WLX_SAS_ACTION_LOCK_WKSTA
;
1017 // DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
1022 case WLX_SAS_TYPE_SCRNSVR_ACTIVITY
:
1023 SetEvent(Session
->hUserActivity
);
1027 DoGenericAction(Session
, wlxAction
);
1033 IN PWLSESSION Session
,
1036 /* Register Ctrl+Alt+Del Hotkey */
1037 if (!RegisterHotKey(hwndSAS
, HK_CTRL_ALT_DEL
, MOD_CONTROL
| MOD_ALT
, VK_DELETE
))
1039 ERR("WL: Unable to register Ctrl+Alt+Del hotkey!\n");
1043 /* Register Ctrl+Shift+Esc (optional) */
1044 Session
->TaskManHotkey
= RegisterHotKey(hwndSAS
, HK_CTRL_SHIFT_ESC
, MOD_CONTROL
| MOD_SHIFT
, VK_ESCAPE
);
1045 if (!Session
->TaskManHotkey
)
1046 WARN("WL: Warning: Unable to register Ctrl+Alt+Esc hotkey!\n");
1053 IN PWLSESSION Session
,
1056 /* Unregister hotkeys */
1057 UnregisterHotKey(hwndSAS
, HK_CTRL_ALT_DEL
);
1059 if (Session
->TaskManHotkey
)
1060 UnregisterHotKey(hwndSAS
, HK_CTRL_SHIFT_ESC
);
1067 CheckForShutdownPrivilege(
1068 IN DWORD RequestingProcessId
)
1073 PPRIVILEGE_SET PrivSet
;
1075 TRACE("CheckForShutdownPrivilege()\n");
1077 Process
= OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, RequestingProcessId
);
1080 WARN("OpenProcess() failed with error %lu\n", GetLastError());
1081 return STATUS_INVALID_HANDLE
;
1083 if (!OpenProcessToken(Process
, TOKEN_QUERY
, &Token
))
1085 WARN("OpenProcessToken() failed with error %lu\n", GetLastError());
1086 CloseHandle(Process
);
1087 return STATUS_INVALID_HANDLE
;
1089 CloseHandle(Process
);
1090 PrivSet
= HeapAlloc(GetProcessHeap(), 0, sizeof(PRIVILEGE_SET
) + sizeof(LUID_AND_ATTRIBUTES
));
1093 ERR("Failed to allocate mem for privilege set\n");
1095 return STATUS_NO_MEMORY
;
1097 PrivSet
->PrivilegeCount
= 1;
1098 PrivSet
->Control
= PRIVILEGE_SET_ALL_NECESSARY
;
1099 if (!LookupPrivilegeValue(NULL
, SE_SHUTDOWN_NAME
, &PrivSet
->Privilege
[0].Luid
))
1101 WARN("LookupPrivilegeValue() failed with error %lu\n", GetLastError());
1102 HeapFree(GetProcessHeap(), 0, PrivSet
);
1104 return STATUS_UNSUCCESSFUL
;
1106 if (!PrivilegeCheck(Token
, PrivSet
, &CheckResult
))
1108 WARN("PrivilegeCheck() failed with error %lu\n", GetLastError());
1109 HeapFree(GetProcessHeap(), 0, PrivSet
);
1111 return STATUS_ACCESS_DENIED
;
1113 HeapFree(GetProcessHeap(), 0, PrivSet
);
1118 WARN("SE_SHUTDOWN privilege not enabled\n");
1119 return STATUS_ACCESS_DENIED
;
1121 return STATUS_SUCCESS
;
1126 HandleMessageBeep(UINT uType
)
1136 EventName
= L
"SystemDefault";
1138 case MB_ICONASTERISK
:
1139 EventName
= L
"SystemAsterisk";
1141 case MB_ICONEXCLAMATION
:
1142 EventName
= L
"SystemExclamation";
1145 EventName
= L
"SystemHand";
1147 case MB_ICONQUESTION
:
1148 EventName
= L
"SystemQuestion";
1151 WARN("Unhandled type %d\n", uType
);
1152 EventName
= L
"SystemDefault";
1155 return PlaySoundRoutine(EventName
, FALSE
, SND_ALIAS
| SND_NOWAIT
| SND_NOSTOP
| SND_ASYNC
);
1167 PWLSESSION Session
= (PWLSESSION
)GetWindowLongPtr(hwndDlg
, GWLP_USERDATA
);
1175 case MAKELONG(MOD_CONTROL
| MOD_ALT
, VK_DELETE
):
1177 TRACE("SAS: CONTROL+ALT+DELETE\n");
1178 if (!Session
->Gina
.UseCtrlAltDelete
)
1180 PostMessageW(Session
->SASWindow
, WLX_WM_SAS
, WLX_SAS_TYPE_CTRL_ALT_DEL
, 0);
1183 case MAKELONG(MOD_CONTROL
| MOD_SHIFT
, VK_ESCAPE
):
1185 TRACE("SAS: CONTROL+SHIFT+ESCAPE\n");
1186 DoGenericAction(Session
, WLX_SAS_ACTION_TASKLIST
);
1194 /* Get the session pointer from the create data */
1195 Session
= (PWLSESSION
)((LPCREATESTRUCT
)lParam
)->lpCreateParams
;
1197 /* Save the Session pointer */
1198 SetWindowLongPtrW(hwndDlg
, GWLP_USERDATA
, (LONG_PTR
)Session
);
1201 return RegisterHotKeys(Session
, hwndDlg
);
1205 if (!GetSetupType())
1206 UnregisterHotKeys(Session
, hwndDlg
);
1209 case WM_SETTINGCHANGE
:
1211 UINT uiAction
= (UINT
)wParam
;
1212 if (uiAction
== SPI_SETSCREENSAVETIMEOUT
1213 || uiAction
== SPI_SETSCREENSAVEACTIVE
)
1215 SetEvent(Session
->hScreenSaverParametersChanged
);
1219 case WM_LOGONNOTIFY
:
1223 case LN_MESSAGE_BEEP
:
1225 return HandleMessageBeep(lParam
);
1227 case LN_SHELL_EXITED
:
1229 /* lParam is the exit code */
1232 SetTimer(hwndDlg
, 1, 1000, NULL
);
1236 case LN_START_SCREENSAVE
:
1238 DispatchSAS(Session
, WLX_SAS_TYPE_SCRNSVR_TIMEOUT
);
1241 case LN_LOCK_WORKSTATION
:
1243 DoGenericAction(Session
, WLX_SAS_ACTION_LOCK_WKSTA
);
1248 ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam
);
1257 KillTimer(hwndDlg
, 1);
1258 StartUserShell(Session
);
1264 DispatchSAS(Session
, (DWORD
)wParam
);
1267 case PM_WINLOGON_EXITWINDOWS
:
1269 UINT Flags
= (UINT
)lParam
;
1270 UINT Action
= Flags
& EWX_ACTION_MASK
;
1273 /* Check parameters */
1276 case EWX_LOGOFF
: wlxAction
= WLX_SAS_ACTION_LOGOFF
; break;
1277 case EWX_SHUTDOWN
: wlxAction
= WLX_SAS_ACTION_SHUTDOWN
; break;
1278 case EWX_REBOOT
: wlxAction
= WLX_SAS_ACTION_SHUTDOWN_REBOOT
; break;
1279 case EWX_POWEROFF
: wlxAction
= WLX_SAS_ACTION_SHUTDOWN_POWER_OFF
; break;
1282 ERR("Invalid ExitWindows action 0x%x\n", Action
);
1283 return STATUS_INVALID_PARAMETER
;
1287 if (WLX_SHUTTINGDOWN(wlxAction
))
1289 NTSTATUS Status
= CheckForShutdownPrivilege((DWORD
)wParam
);
1290 if (!NT_SUCCESS(Status
))
1293 DoGenericAction(Session
, wlxAction
);
1298 return DefWindowProc(hwndDlg
, uMsg
, wParam
, lParam
);
1303 IN OUT PWLSESSION Session
)
1308 if (!SwitchDesktop(Session
->WinlogonDesktop
))
1310 ERR("WL: Failed to switch to winlogon desktop\n");
1314 /* Register SAS window class */
1315 swc
.cbSize
= sizeof(WNDCLASSEXW
);
1316 swc
.style
= CS_SAVEBITS
;
1317 swc
.lpfnWndProc
= SASWindowProc
;
1320 swc
.hInstance
= hAppInstance
;
1323 swc
.hbrBackground
= NULL
;
1324 swc
.lpszMenuName
= NULL
;
1325 swc
.lpszClassName
= WINLOGON_SAS_CLASS
;
1327 if (RegisterClassExW(&swc
) == 0)
1329 ERR("WL: Failed to register SAS window class\n");
1333 /* Create invisible SAS window */
1334 Session
->SASWindow
= CreateWindowExW(
1340 hAppInstance
, Session
);
1341 if (!Session
->SASWindow
)
1343 ERR("WL: Failed to create SAS window\n");
1347 /* Register SAS window to receive SAS notifications */
1348 if (!SetLogonNotifyWindow(Session
->SASWindow
, Session
->InteractiveWindowStation
))
1350 ERR("WL: Failed to register SAS window\n");
1354 if (!SetDefaultLanguage(FALSE
))
1361 UninitializeSAS(Session
);