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