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