Synchronize with trunk r58457.
[reactos.git] / base / system / winlogon / sas.c
1 /*
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)
8 * UPDATE HISTORY:
9 * Created 28/03/2004
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include "winlogon.h"
15
16 WINE_DEFAULT_DEBUG_CHANNEL(winlogon);
17
18 /* GLOBALS ******************************************************************/
19
20 #define WINLOGON_SAS_CLASS L"SAS Window class"
21 #define WINLOGON_SAS_TITLE L"SAS window"
22
23 #define HK_CTRL_ALT_DEL 0
24 #define HK_CTRL_SHIFT_ESC 1
25
26 /* FUNCTIONS ****************************************************************/
27
28 static BOOL
29 StartTaskManager(
30 IN OUT PWLSESSION Session)
31 {
32 LPVOID lpEnvironment;
33 BOOL ret;
34
35 if (!Session->Gina.Functions.WlxStartApplication)
36 return FALSE;
37
38 if (!CreateEnvironmentBlock(
39 &lpEnvironment,
40 Session->UserToken,
41 TRUE))
42 {
43 return FALSE;
44 }
45
46 ret = Session->Gina.Functions.WlxStartApplication(
47 Session->Gina.Context,
48 L"Default",
49 lpEnvironment,
50 L"taskmgr.exe");
51
52 DestroyEnvironmentBlock(lpEnvironment);
53 return ret;
54 }
55
56 static BOOL
57 StartUserShell(
58 IN OUT PWLSESSION Session)
59 {
60 LPVOID lpEnvironment = NULL;
61 BOOLEAN Old;
62 BOOL ret;
63
64 /* Create environment block for the user */
65 if (!CreateEnvironmentBlock(&lpEnvironment, Session->UserToken, TRUE))
66 {
67 WARN("WL: CreateEnvironmentBlock() failed\n");
68 return FALSE;
69 }
70
71 /* Get privilege */
72 /* FIXME: who should do it? winlogon or gina? */
73 /* FIXME: reverting to lower privileges after creating user shell? */
74 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &Old);
75
76 ret = Session->Gina.Functions.WlxActivateUserShell(
77 Session->Gina.Context,
78 L"Default",
79 NULL, /* FIXME */
80 lpEnvironment);
81
82 DestroyEnvironmentBlock(lpEnvironment);
83 return ret;
84 }
85
86
87 BOOL
88 SetDefaultLanguage(
89 IN BOOL UserProfile)
90 {
91 HKEY BaseKey;
92 LPCWSTR SubKey;
93 LPCWSTR ValueName;
94 LONG rc;
95 HKEY hKey = NULL;
96 DWORD dwType, dwSize;
97 LPWSTR Value = NULL;
98 UNICODE_STRING ValueString;
99 NTSTATUS Status;
100 LCID Lcid;
101 BOOL ret = FALSE;
102
103 if (UserProfile)
104 {
105 BaseKey = HKEY_CURRENT_USER;
106 SubKey = L"Control Panel\\International";
107 ValueName = L"Locale";
108 }
109 else
110 {
111 BaseKey = HKEY_LOCAL_MACHINE;
112 SubKey = L"System\\CurrentControlSet\\Control\\Nls\\Language";
113 ValueName = L"Default";
114 }
115
116 rc = RegOpenKeyExW(
117 BaseKey,
118 SubKey,
119 0,
120 KEY_READ,
121 &hKey);
122 if (rc != ERROR_SUCCESS)
123 {
124 TRACE("RegOpenKeyEx() failed with error %lu\n", rc);
125 goto cleanup;
126 }
127 rc = RegQueryValueExW(
128 hKey,
129 ValueName,
130 NULL,
131 &dwType,
132 NULL,
133 &dwSize);
134 if (rc != ERROR_SUCCESS)
135 {
136 TRACE("RegQueryValueEx() failed with error %lu\n", rc);
137 goto cleanup;
138 }
139 else if (dwType != REG_SZ)
140 {
141 TRACE("Wrong type for %S\\%S registry entry (got 0x%lx, expected 0x%x)\n",
142 SubKey, ValueName, dwType, REG_SZ);
143 goto cleanup;
144 }
145
146 Value = HeapAlloc(GetProcessHeap(), 0, dwSize);
147 if (!Value)
148 {
149 TRACE("HeapAlloc() failed\n");
150 goto cleanup;
151 }
152 rc = RegQueryValueExW(
153 hKey,
154 ValueName,
155 NULL,
156 NULL,
157 (LPBYTE)Value,
158 &dwSize);
159 if (rc != ERROR_SUCCESS)
160 {
161 TRACE("RegQueryValueEx() failed with error %lu\n", rc);
162 goto cleanup;
163 }
164
165 /* Convert Value to a Lcid */
166 ValueString.Length = ValueString.MaximumLength = (USHORT)dwSize;
167 ValueString.Buffer = Value;
168 Status = RtlUnicodeStringToInteger(&ValueString, 16, (PULONG)&Lcid);
169 if (!NT_SUCCESS(Status))
170 {
171 TRACE("RtlUnicodeStringToInteger() failed with status 0x%08lx\n", Status);
172 goto cleanup;
173 }
174
175 TRACE("%s language is 0x%08lx\n",
176 UserProfile ? "User" : "System", Lcid);
177 Status = NtSetDefaultLocale(UserProfile, Lcid);
178 if (!NT_SUCCESS(Status))
179 {
180 TRACE("NtSetDefaultLocale() failed with status 0x%08lx\n", Status);
181 goto cleanup;
182 }
183
184 ret = TRUE;
185
186 cleanup:
187 if (hKey)
188 RegCloseKey(hKey);
189 if (Value)
190 HeapFree(GetProcessHeap(), 0, Value);
191 return ret;
192 }
193
194 BOOL
195 PlaySoundRoutine(
196 IN LPCWSTR FileName,
197 IN UINT bLogon,
198 IN UINT Flags)
199 {
200 typedef BOOL (WINAPI *PLAYSOUNDW)(LPCWSTR,HMODULE,DWORD);
201 typedef UINT (WINAPI *WAVEOUTGETNUMDEVS)(VOID);
202 PLAYSOUNDW Play;
203 WAVEOUTGETNUMDEVS waveOutGetNumDevs;
204 UINT NumDevs;
205 HMODULE hLibrary;
206 BOOL Ret = FALSE;
207
208 hLibrary = LoadLibraryW(L"winmm.dll");
209 if (hLibrary)
210 {
211 waveOutGetNumDevs = (WAVEOUTGETNUMDEVS)GetProcAddress(hLibrary, "waveOutGetNumDevs");
212 if (waveOutGetNumDevs)
213 {
214 NumDevs = waveOutGetNumDevs();
215 if (!NumDevs)
216 {
217 if (!bLogon)
218 {
219 Beep(500, 500);
220 }
221 FreeLibrary(hLibrary);
222 return FALSE;
223 }
224 }
225
226 Play = (PLAYSOUNDW)GetProcAddress(hLibrary, "PlaySoundW");
227 if (Play)
228 {
229 Ret = Play(FileName, NULL, Flags);
230 }
231 FreeLibrary(hLibrary);
232 }
233
234 return Ret;
235 }
236
237 DWORD
238 WINAPI
239 PlayLogonSoundThread(
240 IN LPVOID lpParameter)
241 {
242 BYTE TokenUserBuffer[256];
243 PTOKEN_USER pTokenUser = (TOKEN_USER*)TokenUserBuffer;
244 ULONG Length;
245 HKEY hKey;
246 WCHAR wszBuffer[MAX_PATH] = {0};
247 WCHAR wszDest[MAX_PATH];
248 DWORD dwSize = sizeof(wszBuffer), dwType;
249 SERVICE_STATUS_PROCESS Info;
250 UNICODE_STRING SidString;
251 NTSTATUS Status;
252 ULONG Index = 0;
253 SC_HANDLE hSCManager, hService;
254
255 /* Get SID of current user */
256 Status = NtQueryInformationToken((HANDLE)lpParameter,
257 TokenUser,
258 TokenUserBuffer,
259 sizeof(TokenUserBuffer),
260 &Length);
261 if (!NT_SUCCESS(Status))
262 {
263 ERR("NtQueryInformationToken failed: %x!\n", Status);
264 return 0;
265 }
266
267 /* Convert SID to string */
268 RtlInitEmptyUnicodeString(&SidString, wszBuffer, sizeof(wszBuffer));
269 Status = RtlConvertSidToUnicodeString(&SidString, pTokenUser->User.Sid, FALSE);
270 if (!NT_SUCCESS(Status))
271 {
272 ERR("RtlConvertSidToUnicodeString failed: %x!\n", Status);
273 return 0;
274 }
275
276 /* Build path to logon sound registry key.
277 Note: We can't use HKCU here, because Winlogon is owned by SYSTEM user */
278 if (FAILED(StringCbCopyW(wszBuffer + SidString.Length/sizeof(WCHAR),
279 sizeof(wszBuffer) - SidString.Length,
280 L"\\AppEvents\\Schemes\\Apps\\.Default\\WindowsLogon\\.Current")))
281 {
282 /* SID is too long. Should not happen. */
283 ERR("StringCbCopyW failed!\n");
284 return 0;
285 }
286
287 /* Open registry key and query sound path */
288 if (RegOpenKeyExW(HKEY_USERS, wszBuffer, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
289 {
290 ERR("RegOpenKeyExW(%ls) failed!\n", wszBuffer);
291 return 0;
292 }
293
294 if (RegQueryValueExW(hKey, NULL, NULL, &dwType,
295 (LPBYTE)wszBuffer, &dwSize) != ERROR_SUCCESS ||
296 (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
297 {
298 ERR("RegQueryValueExW failed!\n");
299 RegCloseKey(hKey);
300 return 0;
301 }
302
303 RegCloseKey(hKey);
304
305 if (!wszBuffer[0])
306 {
307 /* No sound has been set */
308 ERR("No sound has been set\n");
309 return 0;
310 }
311
312 /* Expand environment variables */
313 if (!ExpandEnvironmentStringsW(wszBuffer, wszDest, MAX_PATH))
314 {
315 ERR("ExpandEnvironmentStringsW failed!\n");
316 return 0;
317 }
318
319 /* Open service manager */
320 hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
321 if (!hSCManager)
322 {
323 ERR("OpenSCManager failed (%x)\n", GetLastError());
324 return 0;
325 }
326
327 /* Open wdmaud service */
328 hService = OpenServiceW(hSCManager, L"wdmaud", GENERIC_READ);
329 if (!hService)
330 {
331 /* Sound is not installed */
332 TRACE("Failed to open wdmaud service (%x)\n", GetLastError());
333 CloseServiceHandle(hSCManager);
334 return 0;
335 }
336
337 /* Wait for wdmaud start */
338 do
339 {
340 if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&Info, sizeof(SERVICE_STATUS_PROCESS), &dwSize))
341 {
342 TRACE("QueryServiceStatusEx failed (%x)\n", GetLastError());
343 break;
344 }
345
346 if (Info.dwCurrentState == SERVICE_RUNNING)
347 break;
348
349 Sleep(1000);
350
351 } while (Index++ < 20);
352
353 CloseServiceHandle(hService);
354 CloseServiceHandle(hSCManager);
355
356 /* If wdmaud is not running exit */
357 if (Info.dwCurrentState != SERVICE_RUNNING)
358 {
359 WARN("wdmaud has not started!\n");
360 return 0;
361 }
362
363 /* Sound subsystem is running. Play logon sound. */
364 TRACE("Playing logon sound: %ls\n", wszDest);
365 PlaySoundRoutine(wszDest, TRUE, SND_FILENAME);
366 return 0;
367 }
368
369 static VOID
370 PlayLogonSound(
371 IN OUT PWLSESSION Session)
372 {
373 HANDLE hThread;
374
375 hThread = CreateThread(NULL, 0, PlayLogonSoundThread, (PVOID)Session->UserToken, 0, NULL);
376 if (hThread)
377 CloseHandle(hThread);
378 }
379
380 static BOOL
381 HandleLogon(
382 IN OUT PWLSESSION Session)
383 {
384 PROFILEINFOW ProfileInfo;
385 BOOL ret = FALSE;
386
387 /* Loading personal settings */
388 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOADINGYOURPERSONALSETTINGS);
389 ProfileInfo.hProfile = INVALID_HANDLE_VALUE;
390 if (0 == (Session->Options & WLX_LOGON_OPT_NO_PROFILE))
391 {
392 if (Session->Profile == NULL
393 || (Session->Profile->dwType != WLX_PROFILE_TYPE_V1_0
394 && Session->Profile->dwType != WLX_PROFILE_TYPE_V2_0))
395 {
396 ERR("WL: Wrong profile\n");
397 goto cleanup;
398 }
399
400 /* Load the user profile */
401 ZeroMemory(&ProfileInfo, sizeof(PROFILEINFOW));
402 ProfileInfo.dwSize = sizeof(PROFILEINFOW);
403 ProfileInfo.dwFlags = 0;
404 ProfileInfo.lpUserName = Session->MprNotifyInfo.pszUserName;
405 ProfileInfo.lpProfilePath = Session->Profile->pszProfile;
406 if (Session->Profile->dwType >= WLX_PROFILE_TYPE_V2_0)
407 {
408 ProfileInfo.lpDefaultPath = Session->Profile->pszNetworkDefaultUserProfile;
409 ProfileInfo.lpServerName = Session->Profile->pszServerName;
410 ProfileInfo.lpPolicyPath = Session->Profile->pszPolicy;
411 }
412
413 if (!LoadUserProfileW(Session->UserToken, &ProfileInfo))
414 {
415 ERR("WL: LoadUserProfileW() failed\n");
416 goto cleanup;
417 }
418 }
419
420 /* Create environment block for the user */
421 if (!CreateUserEnvironment(Session))
422 {
423 WARN("WL: SetUserEnvironment() failed\n");
424 goto cleanup;
425 }
426
427 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_APPLYINGYOURPERSONALSETTINGS);
428 UpdatePerUserSystemParameters(0, TRUE);
429
430 /* Set default language */
431 if (!SetDefaultLanguage(TRUE))
432 {
433 WARN("WL: SetDefaultLanguage() failed\n");
434 goto cleanup;
435 }
436
437 if (!StartUserShell(Session))
438 {
439 //WCHAR StatusMsg[256];
440 WARN("WL: WlxActivateUserShell() failed\n");
441 //LoadStringW(hAppInstance, IDS_FAILEDACTIVATEUSERSHELL, StatusMsg, sizeof(StatusMsg));
442 //MessageBoxW(0, StatusMsg, NULL, MB_ICONERROR);
443 goto cleanup;
444 }
445
446 if (!InitializeScreenSaver(Session))
447 WARN("WL: Failed to initialize screen saver\n");
448
449 Session->hProfileInfo = ProfileInfo.hProfile;
450
451 /* Logon has successed. Play sound. */
452 PlayLogonSound(Session);
453
454 ret = TRUE;
455
456 cleanup:
457 if (Session->Profile)
458 {
459 HeapFree(GetProcessHeap(), 0, Session->Profile->pszProfile);
460 HeapFree(GetProcessHeap(), 0, Session->Profile);
461 }
462 Session->Profile = NULL;
463 if (!ret
464 && ProfileInfo.hProfile != INVALID_HANDLE_VALUE)
465 {
466 UnloadUserProfile(WLSession->UserToken, ProfileInfo.hProfile);
467 }
468 RemoveStatusMessage(Session);
469 if (!ret)
470 {
471 CloseHandle(Session->UserToken);
472 Session->UserToken = NULL;
473 }
474 return ret;
475 }
476
477 #define EWX_ACTION_MASK 0xffffffeb
478 #define EWX_FLAGS_MASK 0x00000014
479
480 typedef struct tagLOGOFF_SHUTDOWN_DATA
481 {
482 UINT Flags;
483 PWLSESSION Session;
484 } LOGOFF_SHUTDOWN_DATA, *PLOGOFF_SHUTDOWN_DATA;
485
486 static DWORD WINAPI
487 LogoffShutdownThread(LPVOID Parameter)
488 {
489 PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA)Parameter;
490
491 if (LSData->Session->UserToken != NULL && !ImpersonateLoggedOnUser(LSData->Session->UserToken))
492 {
493 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
494 return 0;
495 }
496
497 /* Close processes of the interactive user */
498 if (!ExitWindowsEx(
499 EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) |
500 (EWX_LOGOFF == (LSData->Flags & EWX_ACTION_MASK) ? EWX_INTERNAL_FLAG_LOGOFF : 0),
501 0))
502 {
503 ERR("Unable to kill user apps, error %lu\n", GetLastError());
504 RevertToSelf();
505 return 0;
506 }
507
508 /* FIXME: Call ExitWindowsEx() to terminate COM processes */
509
510 if (LSData->Session->UserToken)
511 RevertToSelf();
512
513 return 1;
514 }
515
516
517 static NTSTATUS
518 CreateLogoffSecurityAttributes(
519 OUT PSECURITY_ATTRIBUTES* ppsa)
520 {
521 /* The following code is not working yet and messy */
522 /* Still, it gives some ideas about data types and functions involved and */
523 /* required to set up a SECURITY_DESCRIPTOR for a SECURITY_ATTRIBUTES */
524 /* instance for a thread, to allow that thread to ImpersonateLoggedOnUser(). */
525 /* Specifically THREAD_SET_THREAD_TOKEN is required. */
526 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
527 PSECURITY_ATTRIBUTES psa = 0;
528 BYTE* pMem;
529 PACL pACL;
530 EXPLICIT_ACCESS Access;
531 PSID pEveryoneSID = NULL;
532 static SID_IDENTIFIER_AUTHORITY WorldAuthority = { SECURITY_WORLD_SID_AUTHORITY };
533
534 *ppsa = NULL;
535
536 // Let's first try to enumerate what kind of data we need for this to ever work:
537 // 1. The Winlogon SID, to be able to give it THREAD_SET_THREAD_TOKEN.
538 // 2. The users SID (the user trying to logoff, or rather shut down the system).
539 // 3. At least two EXPLICIT_ACCESS instances:
540 // 3.1 One for Winlogon itself, giving it the rights
541 // required to THREAD_SET_THREAD_TOKEN (as it's needed to successfully call
542 // ImpersonateLoggedOnUser).
543 // 3.2 One for the user, to allow *that* thread to perform its work.
544 // 4. An ACL to hold the these EXPLICIT_ACCESS ACE's.
545 // 5. A SECURITY_DESCRIPTOR to hold the ACL, and finally.
546 // 6. A SECURITY_ATTRIBUTES instance to pull all of this required stuff
547 // together, to hand it to CreateThread.
548 //
549 // However, it seems struct LOGOFF_SHUTDOWN_DATA doesn't contain
550 // these required SID's, why they'd have to be added.
551 // The Winlogon's own SID should probably only be created once,
552 // while the user's SID obviously must be created for each new user.
553 // Might as well store it when the user logs on?
554
555 if(!AllocateAndInitializeSid(&WorldAuthority,
556 1,
557 SECURITY_WORLD_RID,
558 0, 0, 0, 0, 0, 0, 0,
559 &pEveryoneSID))
560 {
561 ERR("Failed to initialize security descriptor for logoff thread!\n");
562 return STATUS_UNSUCCESSFUL;
563 }
564
565 /* set up the required security attributes to be able to shut down */
566 /* To save space and time, allocate a single block of memory holding */
567 /* both SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR */
568 pMem = HeapAlloc(GetProcessHeap(),
569 0,
570 sizeof(SECURITY_ATTRIBUTES) +
571 SECURITY_DESCRIPTOR_MIN_LENGTH +
572 sizeof(ACL));
573 if (!pMem)
574 {
575 ERR("Failed to allocate memory for logoff security descriptor!\n");
576 return STATUS_NO_MEMORY;
577 }
578
579 /* Note that the security descriptor needs to be in _absolute_ format, */
580 /* meaning its members must be pointers to other structures, rather */
581 /* than the relative format using offsets */
582 psa = (PSECURITY_ATTRIBUTES)pMem;
583 SecurityDescriptor = (PSECURITY_DESCRIPTOR)(pMem + sizeof(SECURITY_ATTRIBUTES));
584 pACL = (PACL)(((PBYTE)SecurityDescriptor) + SECURITY_DESCRIPTOR_MIN_LENGTH);
585
586 // Initialize an EXPLICIT_ACCESS structure for an ACE.
587 // The ACE will allow this thread to log off (and shut down the system, currently).
588 ZeroMemory(&Access, sizeof(Access));
589 Access.grfAccessPermissions = THREAD_SET_THREAD_TOKEN;
590 Access.grfAccessMode = SET_ACCESS; // GRANT_ACCESS?
591 Access.grfInheritance = NO_INHERITANCE;
592 Access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
593 Access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
594 Access.Trustee.ptstrName = pEveryoneSID;
595
596 if (SetEntriesInAcl(1, &Access, NULL, &pACL) != ERROR_SUCCESS)
597 {
598 ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n");
599
600 HeapFree(GetProcessHeap(), 0, pMem);
601 return STATUS_UNSUCCESSFUL;
602 }
603
604 if (!InitializeSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
605 {
606 ERR("Failed to initialize security descriptor for logoff thread!\n");
607 HeapFree(GetProcessHeap(), 0, pMem);
608 return STATUS_UNSUCCESSFUL;
609 }
610
611 if (!SetSecurityDescriptorDacl(SecurityDescriptor,
612 TRUE, // bDaclPresent flag
613 pACL,
614 FALSE)) // not a default DACL
615 {
616 ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
617 HeapFree(GetProcessHeap(), 0, pMem);
618 return STATUS_UNSUCCESSFUL;
619 }
620
621 psa->nLength = sizeof(SECURITY_ATTRIBUTES);
622 psa->lpSecurityDescriptor = SecurityDescriptor;
623 psa->bInheritHandle = FALSE;
624
625 *ppsa = psa;
626
627 return STATUS_SUCCESS;
628 }
629
630 static VOID
631 DestroyLogoffSecurityAttributes(
632 IN PSECURITY_ATTRIBUTES psa)
633 {
634 if (psa)
635 {
636 HeapFree(GetProcessHeap(), 0, psa);
637 }
638 }
639
640
641 static NTSTATUS
642 HandleLogoff(
643 IN OUT PWLSESSION Session,
644 IN UINT Flags)
645 {
646 PLOGOFF_SHUTDOWN_DATA LSData;
647 PSECURITY_ATTRIBUTES psa;
648 HANDLE hThread;
649 DWORD exitCode;
650 NTSTATUS Status;
651
652 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_SAVEYOURSETTINGS);
653
654 /* Prepare data for logoff thread */
655 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
656 if (!LSData)
657 {
658 ERR("Failed to allocate mem for thread data\n");
659 return STATUS_NO_MEMORY;
660 }
661 LSData->Flags = Flags;
662 LSData->Session = Session;
663
664 Status = CreateLogoffSecurityAttributes(&psa);
665 if (!NT_SUCCESS(Status))
666 {
667 ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status);
668 HeapFree(GetProcessHeap(), 0, LSData);
669 return Status;
670 }
671
672 /* Run logoff thread */
673 hThread = CreateThread(psa, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
674
675 /* we're done with the SECURITY_DESCRIPTOR */
676 DestroyLogoffSecurityAttributes(psa);
677 psa = NULL;
678
679 if (!hThread)
680 {
681 ERR("Unable to create logoff thread, error %lu\n", GetLastError());
682 HeapFree(GetProcessHeap(), 0, LSData);
683 return STATUS_UNSUCCESSFUL;
684 }
685 WaitForSingleObject(hThread, INFINITE);
686 HeapFree(GetProcessHeap(), 0, LSData);
687 if (!GetExitCodeThread(hThread, &exitCode))
688 {
689 ERR("Unable to get exit code of logoff thread (error %lu)\n", GetLastError());
690 CloseHandle(hThread);
691 return STATUS_UNSUCCESSFUL;
692 }
693 CloseHandle(hThread);
694 if (exitCode == 0)
695 {
696 ERR("Logoff thread returned failure\n");
697 return STATUS_UNSUCCESSFUL;
698 }
699
700 UnloadUserProfile(Session->UserToken, Session->hProfileInfo);
701 CloseHandle(Session->UserToken);
702 UpdatePerUserSystemParameters(0, FALSE);
703 Session->LogonState = STATE_LOGGED_OFF;
704 Session->UserToken = NULL;
705 return STATUS_SUCCESS;
706 }
707
708 static INT_PTR CALLBACK
709 ShutdownComputerWindowProc(
710 IN HWND hwndDlg,
711 IN UINT uMsg,
712 IN WPARAM wParam,
713 IN LPARAM lParam)
714 {
715 UNREFERENCED_PARAMETER(lParam);
716
717 switch (uMsg)
718 {
719 case WM_COMMAND:
720 {
721 switch (LOWORD(wParam))
722 {
723 case IDC_BTNSHTDOWNCOMPUTER:
724 EndDialog(hwndDlg, IDC_BTNSHTDOWNCOMPUTER);
725 return TRUE;
726 }
727 break;
728 }
729 case WM_INITDIALOG:
730 {
731 RemoveMenu(GetSystemMenu(hwndDlg, FALSE), SC_CLOSE, MF_BYCOMMAND);
732 SetFocus(GetDlgItem(hwndDlg, IDC_BTNSHTDOWNCOMPUTER));
733 return TRUE;
734 }
735 }
736 return FALSE;
737 }
738
739 static VOID
740 UninitializeSAS(
741 IN OUT PWLSESSION Session)
742 {
743 if (Session->SASWindow)
744 {
745 DestroyWindow(Session->SASWindow);
746 Session->SASWindow = NULL;
747 }
748 if (Session->hEndOfScreenSaverThread)
749 SetEvent(Session->hEndOfScreenSaverThread);
750 UnregisterClassW(WINLOGON_SAS_CLASS, hAppInstance);
751 }
752
753 NTSTATUS
754 HandleShutdown(
755 IN OUT PWLSESSION Session,
756 IN DWORD wlxAction)
757 {
758 PLOGOFF_SHUTDOWN_DATA LSData;
759 HANDLE hThread;
760 DWORD exitCode;
761
762 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_REACTOSISSHUTTINGDOWN);
763
764 /* Prepare data for shutdown thread */
765 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
766 if (!LSData)
767 {
768 ERR("Failed to allocate mem for thread data\n");
769 return STATUS_NO_MEMORY;
770 }
771 if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF)
772 LSData->Flags = EWX_POWEROFF;
773 else if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
774 LSData->Flags = EWX_REBOOT;
775 else
776 LSData->Flags = EWX_SHUTDOWN;
777 LSData->Session = Session;
778
779 /* Run shutdown thread */
780 hThread = CreateThread(NULL, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
781 if (!hThread)
782 {
783 ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
784 HeapFree(GetProcessHeap(), 0, LSData);
785 return STATUS_UNSUCCESSFUL;
786 }
787 WaitForSingleObject(hThread, INFINITE);
788 HeapFree(GetProcessHeap(), 0, LSData);
789 if (!GetExitCodeThread(hThread, &exitCode))
790 {
791 ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError());
792 CloseHandle(hThread);
793 return STATUS_UNSUCCESSFUL;
794 }
795 CloseHandle(hThread);
796 if (exitCode == 0)
797 {
798 ERR("Shutdown thread returned failure\n");
799 return STATUS_UNSUCCESSFUL;
800 }
801
802 /* Destroy SAS window */
803 UninitializeSAS(Session);
804
805 FIXME("FIXME: Call SMSS API #1\n");
806 if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
807 NtShutdownSystem(ShutdownReboot);
808 else
809 {
810 if (FALSE)
811 {
812 /* FIXME - only show this dialog if it's a shutdown and the computer doesn't support APM */
813 DialogBox(hAppInstance, MAKEINTRESOURCE(IDD_SHUTDOWNCOMPUTER), GetDesktopWindow(), ShutdownComputerWindowProc);
814 }
815 NtShutdownSystem(ShutdownNoReboot);
816 }
817 return STATUS_SUCCESS;
818 }
819
820 static VOID
821 DoGenericAction(
822 IN OUT PWLSESSION Session,
823 IN DWORD wlxAction)
824 {
825 switch (wlxAction)
826 {
827 case WLX_SAS_ACTION_LOGON: /* 0x01 */
828 if (HandleLogon(Session))
829 {
830 SwitchDesktop(Session->ApplicationDesktop);
831 Session->LogonState = STATE_LOGGED_ON;
832 }
833 else
834 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
835 break;
836 case WLX_SAS_ACTION_NONE: /* 0x02 */
837 break;
838 case WLX_SAS_ACTION_LOCK_WKSTA: /* 0x03 */
839 if (Session->Gina.Functions.WlxIsLockOk(Session->Gina.Context))
840 {
841 SwitchDesktop(WLSession->WinlogonDesktop);
842 Session->LogonState = STATE_LOCKED;
843 Session->Gina.Functions.WlxDisplayLockedNotice(Session->Gina.Context);
844 }
845 break;
846 case WLX_SAS_ACTION_LOGOFF: /* 0x04 */
847 case WLX_SAS_ACTION_SHUTDOWN: /* 0x05 */
848 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF: /* 0x0a */
849 case WLX_SAS_ACTION_SHUTDOWN_REBOOT: /* 0x0b */
850 if (Session->LogonState != STATE_LOGGED_OFF)
851 {
852 if (!Session->Gina.Functions.WlxIsLogoffOk(Session->Gina.Context))
853 break;
854 SwitchDesktop(WLSession->WinlogonDesktop);
855 Session->Gina.Functions.WlxLogoff(Session->Gina.Context);
856 if (!NT_SUCCESS(HandleLogoff(Session, EWX_LOGOFF)))
857 {
858 RemoveStatusMessage(Session);
859 break;
860 }
861 }
862 if (WLX_SHUTTINGDOWN(wlxAction))
863 {
864 Session->Gina.Functions.WlxShutdown(Session->Gina.Context, wlxAction);
865 if (!NT_SUCCESS(HandleShutdown(Session, wlxAction)))
866 {
867 RemoveStatusMessage(Session);
868 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
869 }
870 }
871 else
872 {
873 RemoveStatusMessage(Session);
874 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
875 }
876 break;
877 case WLX_SAS_ACTION_TASKLIST: /* 0x07 */
878 SwitchDesktop(WLSession->ApplicationDesktop);
879 StartTaskManager(Session);
880 break;
881 case WLX_SAS_ACTION_UNLOCK_WKSTA: /* 0x08 */
882 SwitchDesktop(WLSession->ApplicationDesktop);
883 Session->LogonState = STATE_LOGGED_ON;
884 break;
885 default:
886 WARN("Unknown SAS action 0x%lx\n", wlxAction);
887 }
888 }
889
890 static VOID
891 DispatchSAS(
892 IN OUT PWLSESSION Session,
893 IN DWORD dwSasType)
894 {
895 DWORD wlxAction = WLX_SAS_ACTION_NONE;
896
897 if (Session->LogonState == STATE_LOGGED_ON)
898 wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOnSAS(Session->Gina.Context, dwSasType, NULL);
899 else if (Session->LogonState == STATE_LOCKED)
900 wlxAction = (DWORD)Session->Gina.Functions.WlxWkstaLockedSAS(Session->Gina.Context, dwSasType);
901 else
902 {
903 /* Display a new dialog (if necessary) */
904 switch (dwSasType)
905 {
906 case WLX_SAS_TYPE_TIMEOUT: /* 0x00 */
907 {
908 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
909 break;
910 }
911 default:
912 {
913 PSID LogonSid = NULL; /* FIXME */
914
915 Session->Options = 0;
916
917 wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOutSAS(
918 Session->Gina.Context,
919 Session->SASAction,
920 &Session->LogonId,
921 LogonSid,
922 &Session->Options,
923 &Session->UserToken,
924 &Session->MprNotifyInfo,
925 (PVOID*)&Session->Profile);
926 break;
927 }
928 }
929 }
930
931 if (dwSasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT)
932 {
933 BOOL bSecure = TRUE;
934 if (!Session->Gina.Functions.WlxScreenSaverNotify(Session->Gina.Context, &bSecure))
935 {
936 /* Skip start of screen saver */
937 SetEvent(Session->hEndOfScreenSaver);
938 }
939 else
940 {
941 StartScreenSaver(Session);
942 if (bSecure)
943 DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
944 }
945 }
946 else if (dwSasType == WLX_SAS_TYPE_SCRNSVR_ACTIVITY)
947 SetEvent(Session->hUserActivity);
948
949 DoGenericAction(Session, wlxAction);
950 }
951
952 static BOOL
953 RegisterHotKeys(
954 IN PWLSESSION Session,
955 IN HWND hwndSAS)
956 {
957 /* Register Ctrl+Alt+Del Hotkey */
958 if (!RegisterHotKey(hwndSAS, HK_CTRL_ALT_DEL, MOD_CONTROL | MOD_ALT, VK_DELETE))
959 {
960 ERR("WL: Unable to register Ctrl+Alt+Del hotkey!\n");
961 return FALSE;
962 }
963
964 /* Register Ctrl+Shift+Esc (optional) */
965 Session->TaskManHotkey = RegisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC, MOD_CONTROL | MOD_SHIFT, VK_ESCAPE);
966 if (!Session->TaskManHotkey)
967 WARN("WL: Warning: Unable to register Ctrl+Alt+Esc hotkey!\n");
968 return TRUE;
969 }
970
971 static BOOL
972 UnregisterHotKeys(
973 IN PWLSESSION Session,
974 IN HWND hwndSAS)
975 {
976 /* Unregister hotkeys */
977 UnregisterHotKey(hwndSAS, HK_CTRL_ALT_DEL);
978
979 if (Session->TaskManHotkey)
980 UnregisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC);
981
982 return TRUE;
983 }
984
985 static NTSTATUS
986 CheckForShutdownPrivilege(
987 IN DWORD RequestingProcessId)
988 {
989 HANDLE Process;
990 HANDLE Token;
991 BOOL CheckResult;
992 PPRIVILEGE_SET PrivSet;
993
994 TRACE("CheckForShutdownPrivilege()\n");
995
996 Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, RequestingProcessId);
997 if (!Process)
998 {
999 WARN("OpenProcess() failed with error %lu\n", GetLastError());
1000 return STATUS_INVALID_HANDLE;
1001 }
1002 if (!OpenProcessToken(Process, TOKEN_QUERY, &Token))
1003 {
1004 WARN("OpenProcessToken() failed with error %lu\n", GetLastError());
1005 CloseHandle(Process);
1006 return STATUS_INVALID_HANDLE;
1007 }
1008 CloseHandle(Process);
1009 PrivSet = HeapAlloc(GetProcessHeap(), 0, sizeof(PRIVILEGE_SET) + sizeof(LUID_AND_ATTRIBUTES));
1010 if (!PrivSet)
1011 {
1012 ERR("Failed to allocate mem for privilege set\n");
1013 CloseHandle(Token);
1014 return STATUS_NO_MEMORY;
1015 }
1016 PrivSet->PrivilegeCount = 1;
1017 PrivSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
1018 if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &PrivSet->Privilege[0].Luid))
1019 {
1020 WARN("LookupPrivilegeValue() failed with error %lu\n", GetLastError());
1021 HeapFree(GetProcessHeap(), 0, PrivSet);
1022 CloseHandle(Token);
1023 return STATUS_UNSUCCESSFUL;
1024 }
1025 if (!PrivilegeCheck(Token, PrivSet, &CheckResult))
1026 {
1027 WARN("PrivilegeCheck() failed with error %lu\n", GetLastError());
1028 HeapFree(GetProcessHeap(), 0, PrivSet);
1029 CloseHandle(Token);
1030 return STATUS_ACCESS_DENIED;
1031 }
1032 HeapFree(GetProcessHeap(), 0, PrivSet);
1033 CloseHandle(Token);
1034
1035 if (!CheckResult)
1036 {
1037 WARN("SE_SHUTDOWN privilege not enabled\n");
1038 return STATUS_ACCESS_DENIED;
1039 }
1040 return STATUS_SUCCESS;
1041 }
1042
1043 BOOL
1044 WINAPI
1045 HandleMessageBeep(UINT uType)
1046 {
1047 LPWSTR EventName;
1048
1049 switch(uType)
1050 {
1051 case 0xFFFFFFFF:
1052 EventName = NULL;
1053 break;
1054 case MB_OK:
1055 EventName = L"SystemDefault";
1056 break;
1057 case MB_ICONASTERISK:
1058 EventName = L"SystemAsterisk";
1059 break;
1060 case MB_ICONEXCLAMATION:
1061 EventName = L"SystemExclamation";
1062 break;
1063 case MB_ICONHAND:
1064 EventName = L"SystemHand";
1065 break;
1066 case MB_ICONQUESTION:
1067 EventName = L"SystemQuestion";
1068 break;
1069 default:
1070 WARN("Unhandled type %d\n", uType);
1071 EventName = L"SystemDefault";
1072 }
1073
1074 return PlaySoundRoutine(EventName, FALSE, SND_ALIAS | SND_NOWAIT | SND_NOSTOP | SND_ASYNC);
1075 }
1076
1077 static LRESULT CALLBACK
1078 SASWindowProc(
1079 IN HWND hwndDlg,
1080 IN UINT uMsg,
1081 IN WPARAM wParam,
1082 IN LPARAM lParam)
1083 {
1084 PWLSESSION Session = (PWLSESSION)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1085
1086 switch (uMsg)
1087 {
1088 case WM_HOTKEY:
1089 {
1090 switch (lParam)
1091 {
1092 case MAKELONG(MOD_CONTROL | MOD_ALT, VK_DELETE):
1093 {
1094 TRACE("SAS: CONTROL+ALT+DELETE\n");
1095 if (!Session->Gina.UseCtrlAltDelete)
1096 break;
1097 PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_CTRL_ALT_DEL, 0);
1098 return TRUE;
1099 }
1100 case MAKELONG(MOD_CONTROL | MOD_SHIFT, VK_ESCAPE):
1101 {
1102 TRACE("SAS: CONTROL+SHIFT+ESCAPE\n");
1103 DoGenericAction(Session, WLX_SAS_ACTION_TASKLIST);
1104 return TRUE;
1105 }
1106 }
1107 break;
1108 }
1109 case WM_CREATE:
1110 {
1111 /* Get the session pointer from the create data */
1112 Session = (PWLSESSION)((LPCREATESTRUCT)lParam)->lpCreateParams;
1113
1114 /* Save the Session pointer */
1115 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)Session);
1116 if (GetSetupType())
1117 return TRUE;
1118 return RegisterHotKeys(Session, hwndDlg);
1119 }
1120 case WM_DESTROY:
1121 {
1122 if (!GetSetupType())
1123 UnregisterHotKeys(Session, hwndDlg);
1124 return TRUE;
1125 }
1126 case WM_SETTINGCHANGE:
1127 {
1128 UINT uiAction = (UINT)wParam;
1129 if (uiAction == SPI_SETSCREENSAVETIMEOUT
1130 || uiAction == SPI_SETSCREENSAVEACTIVE)
1131 {
1132 SetEvent(Session->hScreenSaverParametersChanged);
1133 }
1134 return TRUE;
1135 }
1136 case WM_LOGONNOTIFY:
1137 {
1138 switch(wParam)
1139 {
1140 case LN_MESSAGE_BEEP:
1141 {
1142 return HandleMessageBeep(lParam);
1143 }
1144 case LN_SHELL_EXITED:
1145 {
1146 /* lParam is the exit code */
1147 if(lParam != 1)
1148 {
1149 SetTimer(hwndDlg, 1, 1000, NULL);
1150 }
1151 break;
1152 }
1153 case LN_START_SCREENSAVE:
1154 {
1155 DispatchSAS(Session, WLX_SAS_TYPE_SCRNSVR_TIMEOUT);
1156 break;
1157 }
1158 case LN_LOCK_WORKSTATION:
1159 {
1160 DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
1161 break;
1162 }
1163 default:
1164 {
1165 ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam);
1166 }
1167 }
1168 return 0;
1169 }
1170 case WM_TIMER:
1171 {
1172 if (wParam == 1)
1173 {
1174 KillTimer(hwndDlg, 1);
1175 StartUserShell(Session);
1176 }
1177 break;
1178 }
1179 case WLX_WM_SAS:
1180 {
1181 DispatchSAS(Session, (DWORD)wParam);
1182 return TRUE;
1183 }
1184 case PM_WINLOGON_EXITWINDOWS:
1185 {
1186 UINT Flags = (UINT)lParam;
1187 UINT Action = Flags & EWX_ACTION_MASK;
1188 DWORD wlxAction;
1189
1190 /* Check parameters */
1191 switch (Action)
1192 {
1193 case EWX_LOGOFF: wlxAction = WLX_SAS_ACTION_LOGOFF; break;
1194 case EWX_SHUTDOWN: wlxAction = WLX_SAS_ACTION_SHUTDOWN; break;
1195 case EWX_REBOOT: wlxAction = WLX_SAS_ACTION_SHUTDOWN_REBOOT; break;
1196 case EWX_POWEROFF: wlxAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; break;
1197 default:
1198 {
1199 ERR("Invalid ExitWindows action 0x%x\n", Action);
1200 return STATUS_INVALID_PARAMETER;
1201 }
1202 }
1203
1204 if (WLX_SHUTTINGDOWN(wlxAction))
1205 {
1206 NTSTATUS Status = CheckForShutdownPrivilege((DWORD)wParam);
1207 if (!NT_SUCCESS(Status))
1208 return Status;
1209 }
1210 DoGenericAction(Session, wlxAction);
1211 return 1;
1212 }
1213 }
1214
1215 return DefWindowProc(hwndDlg, uMsg, wParam, lParam);
1216 }
1217
1218 BOOL
1219 InitializeSAS(
1220 IN OUT PWLSESSION Session)
1221 {
1222 WNDCLASSEXW swc;
1223 BOOL ret = FALSE;
1224
1225 if (!SwitchDesktop(Session->WinlogonDesktop))
1226 {
1227 ERR("WL: Failed to switch to winlogon desktop\n");
1228 goto cleanup;
1229 }
1230
1231 /* Register SAS window class */
1232 swc.cbSize = sizeof(WNDCLASSEXW);
1233 swc.style = CS_SAVEBITS;
1234 swc.lpfnWndProc = SASWindowProc;
1235 swc.cbClsExtra = 0;
1236 swc.cbWndExtra = 0;
1237 swc.hInstance = hAppInstance;
1238 swc.hIcon = NULL;
1239 swc.hCursor = NULL;
1240 swc.hbrBackground = NULL;
1241 swc.lpszMenuName = NULL;
1242 swc.lpszClassName = WINLOGON_SAS_CLASS;
1243 swc.hIconSm = NULL;
1244 if (RegisterClassExW(&swc) == 0)
1245 {
1246 ERR("WL: Failed to register SAS window class\n");
1247 goto cleanup;
1248 }
1249
1250 /* Create invisible SAS window */
1251 Session->SASWindow = CreateWindowExW(
1252 0,
1253 WINLOGON_SAS_CLASS,
1254 WINLOGON_SAS_TITLE,
1255 WS_POPUP,
1256 0, 0, 0, 0, 0, 0,
1257 hAppInstance, Session);
1258 if (!Session->SASWindow)
1259 {
1260 ERR("WL: Failed to create SAS window\n");
1261 goto cleanup;
1262 }
1263
1264 /* Register SAS window to receive SAS notifications */
1265 if (!SetLogonNotifyWindow(Session->SASWindow, Session->InteractiveWindowStation))
1266 {
1267 ERR("WL: Failed to register SAS window\n");
1268 goto cleanup;
1269 }
1270
1271 if (!SetDefaultLanguage(FALSE))
1272 return FALSE;
1273
1274 ret = TRUE;
1275
1276 cleanup:
1277 if (!ret)
1278 UninitializeSAS(Session);
1279 return ret;
1280 }