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