- Revert 44301
[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 extern BOOL WINAPI SetLogonNotifyWindow(HWND Wnd, HWINSTA WinSta);
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 BOOL
61 SetDefaultLanguage(
62 IN BOOL UserProfile)
63 {
64 HKEY BaseKey;
65 LPCWSTR SubKey;
66 LPCWSTR ValueName;
67 LONG rc;
68 HKEY hKey = NULL;
69 DWORD dwType, dwSize;
70 LPWSTR Value = NULL;
71 UNICODE_STRING ValueString;
72 NTSTATUS Status;
73 LCID Lcid;
74 BOOL ret = FALSE;
75
76 if (UserProfile)
77 {
78 BaseKey = HKEY_CURRENT_USER;
79 SubKey = L"Control Panel\\International";
80 ValueName = L"Locale";
81 }
82 else
83 {
84 BaseKey = HKEY_LOCAL_MACHINE;
85 SubKey = L"System\\CurrentControlSet\\Control\\Nls\\Language";
86 ValueName = L"Default";
87 }
88
89 rc = RegOpenKeyExW(
90 BaseKey,
91 SubKey,
92 0,
93 KEY_READ,
94 &hKey);
95 if (rc != ERROR_SUCCESS)
96 {
97 TRACE("RegOpenKeyEx() failed with error %lu\n", rc);
98 goto cleanup;
99 }
100 rc = RegQueryValueExW(
101 hKey,
102 ValueName,
103 NULL,
104 &dwType,
105 NULL,
106 &dwSize);
107 if (rc != ERROR_SUCCESS)
108 {
109 TRACE("RegQueryValueEx() failed with error %lu\n", rc);
110 goto cleanup;
111 }
112 else if (dwType != REG_SZ)
113 {
114 TRACE("Wrong type for %S\\%S registry entry (got 0x%lx, expected 0x%x)\n",
115 SubKey, ValueName, dwType, REG_SZ);
116 goto cleanup;
117 }
118
119 Value = HeapAlloc(GetProcessHeap(), 0, dwSize);
120 if (!Value)
121 {
122 TRACE("HeapAlloc() failed\n");
123 goto cleanup;
124 }
125 rc = RegQueryValueExW(
126 hKey,
127 ValueName,
128 NULL,
129 NULL,
130 (LPBYTE)Value,
131 &dwSize);
132 if (rc != ERROR_SUCCESS)
133 {
134 TRACE("RegQueryValueEx() failed with error %lu\n", rc);
135 goto cleanup;
136 }
137
138 /* Convert Value to a Lcid */
139 ValueString.Length = ValueString.MaximumLength = (USHORT)dwSize;
140 ValueString.Buffer = Value;
141 Status = RtlUnicodeStringToInteger(&ValueString, 16, (PULONG)&Lcid);
142 if (!NT_SUCCESS(Status))
143 {
144 TRACE("RtlUnicodeStringToInteger() failed with status 0x%08lx\n", Status);
145 goto cleanup;
146 }
147
148 TRACE("%s language is 0x%08lx\n",
149 UserProfile ? "User" : "System", Lcid);
150 Status = NtSetDefaultLocale(UserProfile, Lcid);
151 if (!NT_SUCCESS(Status))
152 {
153 TRACE("NtSetDefaultLocale() failed with status 0x%08lx\n", Status);
154 goto cleanup;
155 }
156
157 ret = TRUE;
158
159 cleanup:
160 if (hKey)
161 RegCloseKey(hKey);
162 if (Value)
163 HeapFree(GetProcessHeap(), 0, Value);
164 return ret;
165 }
166
167 static BOOL
168 HandleLogon(
169 IN OUT PWLSESSION Session)
170 {
171 PROFILEINFOW ProfileInfo;
172 LPVOID lpEnvironment = NULL;
173 LPWSTR lpFullEnv = NULL;
174 LPCWSTR wstr;
175 SIZE_T EnvBlockSize = 0, ProfileSize = 0;
176 BOOLEAN Old;
177 BOOL ret = FALSE;
178
179 /* Loading personal settings */
180 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOADINGYOURPERSONALSETTINGS);
181 ProfileInfo.hProfile = INVALID_HANDLE_VALUE;
182 if (0 == (Session->Options & WLX_LOGON_OPT_NO_PROFILE))
183 {
184 if (Session->Profile == NULL
185 || (Session->Profile->dwType != WLX_PROFILE_TYPE_V1_0
186 && Session->Profile->dwType != WLX_PROFILE_TYPE_V2_0))
187 {
188 ERR("WL: Wrong profile\n");
189 goto cleanup;
190 }
191
192 /* Load the user profile */
193 ZeroMemory(&ProfileInfo, sizeof(PROFILEINFOW));
194 ProfileInfo.dwSize = sizeof(PROFILEINFOW);
195 ProfileInfo.dwFlags = 0;
196 ProfileInfo.lpUserName = Session->MprNotifyInfo.pszUserName;
197 ProfileInfo.lpProfilePath = Session->Profile->pszProfile;
198 if (Session->Profile->dwType >= WLX_PROFILE_TYPE_V2_0)
199 {
200 ProfileInfo.lpDefaultPath = Session->Profile->pszNetworkDefaultUserProfile;
201 ProfileInfo.lpServerName = Session->Profile->pszServerName;
202 ProfileInfo.lpPolicyPath = Session->Profile->pszPolicy;
203 }
204
205 if (!LoadUserProfileW(Session->UserToken, &ProfileInfo))
206 {
207 ERR("WL: LoadUserProfileW() failed\n");
208 goto cleanup;
209 }
210 }
211
212 /* Create environment block for the user */
213 if (!CreateEnvironmentBlock(
214 &lpEnvironment,
215 Session->UserToken,
216 TRUE))
217 {
218 WARN("WL: CreateEnvironmentBlock() failed\n");
219 goto cleanup;
220 }
221
222 if (Session->Profile->dwType == WLX_PROFILE_TYPE_V2_0 && Session->Profile->pszEnvironment)
223 {
224 /* Count required size for full environment */
225 wstr = (LPCWSTR)lpEnvironment;
226 while (*wstr != UNICODE_NULL)
227 {
228 SIZE_T size = wcslen(wstr) + 1;
229 wstr += size;
230 EnvBlockSize += size;
231 }
232 wstr = Session->Profile->pszEnvironment;
233 while (*wstr != UNICODE_NULL)
234 {
235 SIZE_T size = wcslen(wstr) + 1;
236 wstr += size;
237 ProfileSize += size;
238 }
239
240 /* Allocate enough memory */
241 lpFullEnv = HeapAlloc(GetProcessHeap, 0, (EnvBlockSize + ProfileSize + 1) * sizeof(WCHAR));
242 if (!lpFullEnv)
243 {
244 TRACE("HeapAlloc() failed\n");
245 goto cleanup;
246 }
247
248 /* Fill user environment block */
249 CopyMemory(
250 lpFullEnv,
251 lpEnvironment,
252 EnvBlockSize * sizeof(WCHAR));
253 CopyMemory(
254 &lpFullEnv[EnvBlockSize],
255 Session->Profile->pszEnvironment,
256 ProfileSize * sizeof(WCHAR));
257 lpFullEnv[EnvBlockSize + ProfileSize] = UNICODE_NULL;
258 }
259 else
260 {
261 lpFullEnv = (LPWSTR)lpEnvironment;
262 }
263
264 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_APPLYINGYOURPERSONALSETTINGS);
265 UpdatePerUserSystemParameters(0, TRUE);
266
267 /* Set default language */
268 if (!SetDefaultLanguage(TRUE))
269 {
270 WARN("WL: SetDefaultLanguage() failed\n");
271 goto cleanup;
272 }
273
274 /* Get privilege */
275 /* FIXME: who should do it? winlogon or gina? */
276 /* FIXME: reverting to lower privileges after creating user shell? */
277 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &Old);
278
279 if (!Session->Gina.Functions.WlxActivateUserShell(
280 Session->Gina.Context,
281 L"Default",
282 NULL, /* FIXME */
283 lpFullEnv))
284 {
285 //WCHAR StatusMsg[256];
286 WARN("WL: WlxActivateUserShell() failed\n");
287 //LoadStringW(hAppInstance, IDS_FAILEDACTIVATEUSERSHELL, StatusMsg, sizeof(StatusMsg));
288 //MessageBoxW(0, StatusMsg, NULL, MB_ICONERROR);
289 goto cleanup;
290 }
291
292 if (!InitializeScreenSaver(Session))
293 WARN("WL: Failed to initialize screen saver\n");
294
295 Session->hProfileInfo = ProfileInfo.hProfile;
296 ret = TRUE;
297
298 cleanup:
299 if (Session->Profile)
300 {
301 HeapFree(GetProcessHeap(), 0, Session->Profile->pszProfile);
302 HeapFree(GetProcessHeap(), 0, Session->Profile);
303 }
304 Session->Profile = NULL;
305 if (!ret
306 && ProfileInfo.hProfile != INVALID_HANDLE_VALUE)
307 {
308 UnloadUserProfile(WLSession->UserToken, ProfileInfo.hProfile);
309 }
310 if (lpFullEnv != lpEnvironment)
311 HeapFree(GetProcessHeap(), 0, lpFullEnv);
312 if (lpEnvironment)
313 DestroyEnvironmentBlock(lpEnvironment);
314 RemoveStatusMessage(Session);
315 if (!ret)
316 {
317 CloseHandle(Session->UserToken);
318 Session->UserToken = NULL;
319 }
320 return ret;
321 }
322
323 #define EWX_ACTION_MASK 0xffffffeb
324 #define EWX_FLAGS_MASK 0x00000014
325
326 typedef struct tagLOGOFF_SHUTDOWN_DATA
327 {
328 UINT Flags;
329 PWLSESSION Session;
330 } LOGOFF_SHUTDOWN_DATA, *PLOGOFF_SHUTDOWN_DATA;
331
332 static DWORD WINAPI
333 LogoffShutdownThread(LPVOID Parameter)
334 {
335 PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA)Parameter;
336
337 if (LSData->Session->UserToken != NULL && !ImpersonateLoggedOnUser(LSData->Session->UserToken))
338 {
339 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
340 return 0;
341 }
342
343 /* Close processes of the interactive user */
344 if (!ExitWindowsEx(
345 EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) |
346 (EWX_LOGOFF == (LSData->Flags & EWX_ACTION_MASK) ? EWX_INTERNAL_FLAG_LOGOFF : 0),
347 0))
348 {
349 ERR("Unable to kill user apps, error %lu\n", GetLastError());
350 RevertToSelf();
351 return 0;
352 }
353
354 /* FIXME: Call ExitWindowsEx() to terminate COM processes */
355
356 if (LSData->Session->UserToken)
357 RevertToSelf();
358
359 return 1;
360 }
361
362
363 static NTSTATUS
364 CreateLogoffSecurityAttributes(
365 OUT PSECURITY_ATTRIBUTES* ppsa)
366 {
367 /* The following code is not working yet and messy */
368 /* Still, it gives some ideas about data types and functions involved and */
369 /* required to set up a SECURITY_DESCRIPTOR for a SECURITY_ATTRIBUTES */
370 /* instance for a thread, to allow that thread to ImpersonateLoggedOnUser(). */
371 /* Specifically THREAD_SET_THREAD_TOKEN is required. */
372 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
373 PSECURITY_ATTRIBUTES psa = 0;
374 BYTE* pMem;
375 PACL pACL;
376 EXPLICIT_ACCESS Access;
377 PSID pEveryoneSID = NULL;
378 static SID_IDENTIFIER_AUTHORITY WorldAuthority = { SECURITY_WORLD_SID_AUTHORITY };
379
380 *ppsa = NULL;
381
382 // Let's first try to enumerate what kind of data we need for this to ever work:
383 // 1. The Winlogon SID, to be able to give it THREAD_SET_THREAD_TOKEN.
384 // 2. The users SID (the user trying to logoff, or rather shut down the system).
385 // 3. At least two EXPLICIT_ACCESS instances:
386 // 3.1 One for Winlogon itself, giving it the rights
387 // required to THREAD_SET_THREAD_TOKEN (as it's needed to successfully call
388 // ImpersonateLoggedOnUser).
389 // 3.2 One for the user, to allow *that* thread to perform its work.
390 // 4. An ACL to hold the these EXPLICIT_ACCESS ACE's.
391 // 5. A SECURITY_DESCRIPTOR to hold the ACL, and finally.
392 // 6. A SECURITY_ATTRIBUTES instance to pull all of this required stuff
393 // together, to hand it to CreateThread.
394 //
395 // However, it seems struct LOGOFF_SHUTDOWN_DATA doesn't contain
396 // these required SID's, why they'd have to be added.
397 // The Winlogon's own SID should probably only be created once,
398 // while the user's SID obviously must be created for each new user.
399 // Might as well store it when the user logs on?
400
401 if(!AllocateAndInitializeSid(&WorldAuthority,
402 1,
403 SECURITY_WORLD_RID,
404 0, 0, 0, 0, 0, 0, 0,
405 &pEveryoneSID))
406 {
407 ERR("Failed to initialize security descriptor for logoff thread!\n");
408 return STATUS_UNSUCCESSFUL;
409 }
410
411 /* set up the required security attributes to be able to shut down */
412 /* To save space and time, allocate a single block of memory holding */
413 /* both SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR */
414 pMem = HeapAlloc(GetProcessHeap(),
415 0,
416 sizeof(SECURITY_ATTRIBUTES) +
417 SECURITY_DESCRIPTOR_MIN_LENGTH +
418 sizeof(ACL));
419 if (!pMem)
420 {
421 ERR("Failed to allocate memory for logoff security descriptor!\n");
422 return STATUS_NO_MEMORY;
423 }
424
425 /* Note that the security descriptor needs to be in _absolute_ format, */
426 /* meaning its members must be pointers to other structures, rather */
427 /* than the relative format using offsets */
428 psa = (PSECURITY_ATTRIBUTES)pMem;
429 SecurityDescriptor = (PSECURITY_DESCRIPTOR)(pMem + sizeof(SECURITY_ATTRIBUTES));
430 pACL = (PACL)(((PBYTE)SecurityDescriptor) + SECURITY_DESCRIPTOR_MIN_LENGTH);
431
432 // Initialize an EXPLICIT_ACCESS structure for an ACE.
433 // The ACE will allow this thread to log off (and shut down the system, currently).
434 ZeroMemory(&Access, sizeof(Access));
435 Access.grfAccessPermissions = THREAD_SET_THREAD_TOKEN;
436 Access.grfAccessMode = SET_ACCESS; // GRANT_ACCESS?
437 Access.grfInheritance = NO_INHERITANCE;
438 Access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
439 Access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
440 Access.Trustee.ptstrName = pEveryoneSID;
441
442 if (SetEntriesInAcl(1, &Access, NULL, &pACL) != ERROR_SUCCESS)
443 {
444 ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n");
445
446 HeapFree(GetProcessHeap(), 0, pMem);
447 return STATUS_UNSUCCESSFUL;
448 }
449
450 if (!InitializeSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
451 {
452 ERR("Failed to initialize security descriptor for logoff thread!\n");
453 HeapFree(GetProcessHeap(), 0, pMem);
454 return STATUS_UNSUCCESSFUL;
455 }
456
457 if (!SetSecurityDescriptorDacl(SecurityDescriptor,
458 TRUE, // bDaclPresent flag
459 pACL,
460 FALSE)) // not a default DACL
461 {
462 ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
463 HeapFree(GetProcessHeap(), 0, pMem);
464 return STATUS_UNSUCCESSFUL;
465 }
466
467 psa->nLength = sizeof(SECURITY_ATTRIBUTES);
468 psa->lpSecurityDescriptor = SecurityDescriptor;
469 psa->bInheritHandle = FALSE;
470
471 *ppsa = psa;
472
473 return STATUS_SUCCESS;
474 }
475
476 static VOID
477 DestroyLogoffSecurityAttributes(
478 IN PSECURITY_ATTRIBUTES psa)
479 {
480 if (psa)
481 {
482 HeapFree(GetProcessHeap(), 0, psa);
483 }
484 }
485
486
487 static NTSTATUS
488 HandleLogoff(
489 IN OUT PWLSESSION Session,
490 IN UINT Flags)
491 {
492 PLOGOFF_SHUTDOWN_DATA LSData;
493 PSECURITY_ATTRIBUTES psa;
494 HANDLE hThread;
495 DWORD exitCode;
496 NTSTATUS Status;
497
498 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_SAVEYOURSETTINGS);
499
500 /* Prepare data for logoff thread */
501 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
502 if (!LSData)
503 {
504 ERR("Failed to allocate mem for thread data\n");
505 return STATUS_NO_MEMORY;
506 }
507 LSData->Flags = Flags;
508 LSData->Session = Session;
509
510 Status = CreateLogoffSecurityAttributes(&psa);
511 if (!NT_SUCCESS(Status))
512 {
513 ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status);
514 HeapFree(GetProcessHeap(), 0, LSData);
515 return Status;
516 }
517
518 /* Run logoff thread */
519 hThread = CreateThread(psa, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
520
521 /* we're done with the SECURITY_DESCRIPTOR */
522 DestroyLogoffSecurityAttributes(psa);
523 psa = NULL;
524
525 if (!hThread)
526 {
527 ERR("Unable to create logoff thread, error %lu\n", GetLastError());
528 HeapFree(GetProcessHeap(), 0, LSData);
529 return STATUS_UNSUCCESSFUL;
530 }
531 WaitForSingleObject(hThread, INFINITE);
532 HeapFree(GetProcessHeap(), 0, LSData);
533 if (!GetExitCodeThread(hThread, &exitCode))
534 {
535 ERR("Unable to get exit code of logoff thread (error %lu)\n", GetLastError());
536 CloseHandle(hThread);
537 return STATUS_UNSUCCESSFUL;
538 }
539 CloseHandle(hThread);
540 if (exitCode == 0)
541 {
542 ERR("Logoff thread returned failure\n");
543 return STATUS_UNSUCCESSFUL;
544 }
545
546 UnloadUserProfile(Session->UserToken, Session->hProfileInfo);
547 CloseHandle(Session->UserToken);
548 UpdatePerUserSystemParameters(0, FALSE);
549 Session->LogonStatus = WKSTA_IS_LOGGED_OFF;
550 Session->UserToken = NULL;
551 return STATUS_SUCCESS;
552 }
553
554 static INT_PTR CALLBACK
555 ShutdownComputerWindowProc(
556 IN HWND hwndDlg,
557 IN UINT uMsg,
558 IN WPARAM wParam,
559 IN LPARAM lParam)
560 {
561 UNREFERENCED_PARAMETER(lParam);
562
563 switch (uMsg)
564 {
565 case WM_COMMAND:
566 {
567 switch (LOWORD(wParam))
568 {
569 case IDC_BTNSHTDOWNCOMPUTER:
570 EndDialog(hwndDlg, IDC_BTNSHTDOWNCOMPUTER);
571 return TRUE;
572 }
573 break;
574 }
575 case WM_INITDIALOG:
576 {
577 RemoveMenu(GetSystemMenu(hwndDlg, FALSE), SC_CLOSE, MF_BYCOMMAND);
578 SetFocus(GetDlgItem(hwndDlg, IDC_BTNSHTDOWNCOMPUTER));
579 return TRUE;
580 }
581 }
582 return FALSE;
583 }
584
585 static VOID
586 UninitializeSAS(
587 IN OUT PWLSESSION Session)
588 {
589 if (Session->SASWindow)
590 {
591 DestroyWindow(Session->SASWindow);
592 Session->SASWindow = NULL;
593 }
594 if (Session->hEndOfScreenSaverThread)
595 SetEvent(Session->hEndOfScreenSaverThread);
596 UnregisterClassW(WINLOGON_SAS_CLASS, hAppInstance);
597 }
598
599 NTSTATUS
600 HandleShutdown(
601 IN OUT PWLSESSION Session,
602 IN DWORD wlxAction)
603 {
604 PLOGOFF_SHUTDOWN_DATA LSData;
605 HANDLE hThread;
606 DWORD exitCode;
607
608 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_REACTOSISSHUTTINGDOWN);
609
610 /* Prepare data for shutdown thread */
611 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
612 if (!LSData)
613 {
614 ERR("Failed to allocate mem for thread data\n");
615 return STATUS_NO_MEMORY;
616 }
617 if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF)
618 LSData->Flags = EWX_POWEROFF;
619 else if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
620 LSData->Flags = EWX_REBOOT;
621 else
622 LSData->Flags = EWX_SHUTDOWN;
623 LSData->Session = Session;
624
625 /* Run shutdown thread */
626 hThread = CreateThread(NULL, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
627 if (!hThread)
628 {
629 ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
630 HeapFree(GetProcessHeap(), 0, LSData);
631 return STATUS_UNSUCCESSFUL;
632 }
633 WaitForSingleObject(hThread, INFINITE);
634 HeapFree(GetProcessHeap(), 0, LSData);
635 if (!GetExitCodeThread(hThread, &exitCode))
636 {
637 ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError());
638 CloseHandle(hThread);
639 return STATUS_UNSUCCESSFUL;
640 }
641 CloseHandle(hThread);
642 if (exitCode == 0)
643 {
644 ERR("Shutdown thread returned failure\n");
645 return STATUS_UNSUCCESSFUL;
646 }
647
648 /* Destroy SAS window */
649 UninitializeSAS(Session);
650
651 FIXME("FIXME: Call SMSS API #1\n");
652 if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
653 NtShutdownSystem(ShutdownReboot);
654 else
655 {
656 if (FALSE)
657 {
658 /* FIXME - only show this dialog if it's a shutdown and the computer doesn't support APM */
659 DialogBox(hAppInstance, MAKEINTRESOURCE(IDD_SHUTDOWNCOMPUTER), GetDesktopWindow(), ShutdownComputerWindowProc);
660 }
661 NtShutdownSystem(ShutdownNoReboot);
662 }
663 return STATUS_SUCCESS;
664 }
665
666 static VOID
667 DoGenericAction(
668 IN OUT PWLSESSION Session,
669 IN DWORD wlxAction)
670 {
671 switch (wlxAction)
672 {
673 case WLX_SAS_ACTION_LOGON: /* 0x01 */
674 if (HandleLogon(Session))
675 {
676 SwitchDesktop(Session->ApplicationDesktop);
677 Session->LogonStatus = WKSTA_IS_LOGGED_ON;
678 }
679 else
680 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
681 break;
682 case WLX_SAS_ACTION_NONE: /* 0x02 */
683 break;
684 case WLX_SAS_ACTION_LOCK_WKSTA: /* 0x03 */
685 if (Session->Gina.Functions.WlxIsLockOk(Session->Gina.Context))
686 {
687 SwitchDesktop(WLSession->WinlogonDesktop);
688 Session->LogonStatus = WKSTA_IS_LOCKED;
689 Session->Gina.Functions.WlxDisplayLockedNotice(Session->Gina.Context);
690 }
691 break;
692 case WLX_SAS_ACTION_LOGOFF: /* 0x04 */
693 case WLX_SAS_ACTION_SHUTDOWN: /* 0x05 */
694 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF: /* 0x0a */
695 case WLX_SAS_ACTION_SHUTDOWN_REBOOT: /* 0x0b */
696 if (Session->LogonStatus != WKSTA_IS_LOGGED_OFF)
697 {
698 if (!Session->Gina.Functions.WlxIsLogoffOk(Session->Gina.Context))
699 break;
700 SwitchDesktop(WLSession->WinlogonDesktop);
701 Session->Gina.Functions.WlxLogoff(Session->Gina.Context);
702 if (!NT_SUCCESS(HandleLogoff(Session, EWX_LOGOFF)))
703 {
704 RemoveStatusMessage(Session);
705 break;
706 }
707 }
708 if (WLX_SHUTTINGDOWN(wlxAction))
709 {
710 Session->Gina.Functions.WlxShutdown(Session->Gina.Context, wlxAction);
711 if (!NT_SUCCESS(HandleShutdown(Session, wlxAction)))
712 {
713 RemoveStatusMessage(Session);
714 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
715 }
716 }
717 else
718 {
719 RemoveStatusMessage(Session);
720 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
721 }
722 break;
723 case WLX_SAS_ACTION_TASKLIST: /* 0x07 */
724 SwitchDesktop(WLSession->ApplicationDesktop);
725 StartTaskManager(Session);
726 break;
727 case WLX_SAS_ACTION_UNLOCK_WKSTA: /* 0x08 */
728 SwitchDesktop(WLSession->ApplicationDesktop);
729 Session->LogonStatus = WKSTA_IS_LOGGED_ON;
730 break;
731 default:
732 WARN("Unknown SAS action 0x%lx\n", wlxAction);
733 }
734 }
735
736 static VOID
737 DispatchSAS(
738 IN OUT PWLSESSION Session,
739 IN DWORD dwSasType)
740 {
741 DWORD wlxAction = WLX_SAS_ACTION_NONE;
742
743 if (Session->LogonStatus == WKSTA_IS_LOGGED_ON)
744 wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOnSAS(Session->Gina.Context, dwSasType, NULL);
745 else if (Session->LogonStatus == WKSTA_IS_LOCKED)
746 wlxAction = (DWORD)Session->Gina.Functions.WlxWkstaLockedSAS(Session->Gina.Context, dwSasType);
747 else
748 {
749 /* Display a new dialog (if necessary) */
750 switch (dwSasType)
751 {
752 case WLX_SAS_TYPE_TIMEOUT: /* 0x00 */
753 {
754 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
755 break;
756 }
757 default:
758 {
759 PSID LogonSid = NULL; /* FIXME */
760
761 Session->Options = 0;
762
763 wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOutSAS(
764 Session->Gina.Context,
765 Session->SASAction,
766 &Session->LogonId,
767 LogonSid,
768 &Session->Options,
769 &Session->UserToken,
770 &Session->MprNotifyInfo,
771 (PVOID*)&Session->Profile);
772 break;
773 }
774 }
775 }
776
777 if (dwSasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT)
778 {
779 BOOL bSecure = TRUE;
780 if (!Session->Gina.Functions.WlxScreenSaverNotify(Session->Gina.Context, &bSecure))
781 {
782 /* Skip start of screen saver */
783 SetEvent(Session->hEndOfScreenSaver);
784 }
785 else
786 {
787 if (bSecure)
788 DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
789 StartScreenSaver(Session);
790 }
791 }
792 else if (dwSasType == WLX_SAS_TYPE_SCRNSVR_ACTIVITY)
793 SetEvent(Session->hUserActivity);
794
795 DoGenericAction(Session, wlxAction);
796 }
797
798 static BOOL
799 RegisterHotKeys(
800 IN PWLSESSION Session,
801 IN HWND hwndSAS)
802 {
803 /* Register Ctrl+Alt+Del Hotkey */
804 if (!RegisterHotKey(hwndSAS, HK_CTRL_ALT_DEL, MOD_CONTROL | MOD_ALT, VK_DELETE))
805 {
806 ERR("WL: Unable to register Ctrl+Alt+Del hotkey!\n");
807 return FALSE;
808 }
809
810 /* Register Ctrl+Shift+Esc (optional) */
811 Session->TaskManHotkey = RegisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC, MOD_CONTROL | MOD_SHIFT, VK_ESCAPE);
812 if (!Session->TaskManHotkey)
813 WARN("WL: Warning: Unable to register Ctrl+Alt+Esc hotkey!\n");
814 return TRUE;
815 }
816
817 static BOOL
818 UnregisterHotKeys(
819 IN PWLSESSION Session,
820 IN HWND hwndSAS)
821 {
822 /* Unregister hotkeys */
823 UnregisterHotKey(hwndSAS, HK_CTRL_ALT_DEL);
824
825 if (Session->TaskManHotkey)
826 UnregisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC);
827
828 return TRUE;
829 }
830
831 static NTSTATUS
832 CheckForShutdownPrivilege(
833 IN DWORD RequestingProcessId)
834 {
835 HANDLE Process;
836 HANDLE Token;
837 BOOL CheckResult;
838 PPRIVILEGE_SET PrivSet;
839
840 TRACE("CheckForShutdownPrivilege()\n");
841
842 Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, RequestingProcessId);
843 if (!Process)
844 {
845 WARN("OpenProcess() failed with error %lu\n", GetLastError());
846 return STATUS_INVALID_HANDLE;
847 }
848 if (!OpenProcessToken(Process, TOKEN_QUERY, &Token))
849 {
850 WARN("OpenProcessToken() failed with error %lu\n", GetLastError());
851 CloseHandle(Process);
852 return STATUS_INVALID_HANDLE;
853 }
854 CloseHandle(Process);
855 PrivSet = HeapAlloc(GetProcessHeap(), 0, sizeof(PRIVILEGE_SET) + sizeof(LUID_AND_ATTRIBUTES));
856 if (!PrivSet)
857 {
858 ERR("Failed to allocate mem for privilege set\n");
859 CloseHandle(Token);
860 return STATUS_NO_MEMORY;
861 }
862 PrivSet->PrivilegeCount = 1;
863 PrivSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
864 if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &PrivSet->Privilege[0].Luid))
865 {
866 WARN("LookupPrivilegeValue() failed with error %lu\n", GetLastError());
867 HeapFree(GetProcessHeap(), 0, PrivSet);
868 CloseHandle(Token);
869 return STATUS_UNSUCCESSFUL;
870 }
871 if (!PrivilegeCheck(Token, PrivSet, &CheckResult))
872 {
873 WARN("PrivilegeCheck() failed with error %lu\n", GetLastError());
874 HeapFree(GetProcessHeap(), 0, PrivSet);
875 CloseHandle(Token);
876 return STATUS_ACCESS_DENIED;
877 }
878 HeapFree(GetProcessHeap(), 0, PrivSet);
879 CloseHandle(Token);
880
881 if (!CheckResult)
882 {
883 WARN("SE_SHUTDOWN privilege not enabled\n");
884 return STATUS_ACCESS_DENIED;
885 }
886 return STATUS_SUCCESS;
887 }
888
889 static LRESULT CALLBACK
890 SASWindowProc(
891 IN HWND hwndDlg,
892 IN UINT uMsg,
893 IN WPARAM wParam,
894 IN LPARAM lParam)
895 {
896 PWLSESSION Session = (PWLSESSION)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
897
898 switch (uMsg)
899 {
900 case WM_HOTKEY:
901 {
902 switch (lParam)
903 {
904 case MAKELONG(MOD_CONTROL | MOD_ALT, VK_DELETE):
905 {
906 TRACE("SAS: CONTROL+ALT+DELETE\n");
907 if (!Session->Gina.UseCtrlAltDelete)
908 break;
909 PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_CTRL_ALT_DEL, 0);
910 return TRUE;
911 }
912 case MAKELONG(MOD_CONTROL | MOD_SHIFT, VK_ESCAPE):
913 {
914 TRACE("SAS: CONTROL+SHIFT+ESCAPE\n");
915 DoGenericAction(Session, WLX_SAS_ACTION_TASKLIST);
916 return TRUE;
917 }
918 }
919 break;
920 }
921 case WM_CREATE:
922 {
923 /* Get the session pointer from the create data */
924 Session = (PWLSESSION)((LPCREATESTRUCT)lParam)->lpCreateParams;
925
926 /* Save the Session pointer */
927 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)Session);
928 if (GetSetupType())
929 return TRUE;
930 return RegisterHotKeys(Session, hwndDlg);
931 }
932 case WM_DESTROY:
933 {
934 if (!GetSetupType())
935 UnregisterHotKeys(Session, hwndDlg);
936 return TRUE;
937 }
938 case WM_SETTINGCHANGE:
939 {
940 UINT uiAction = (UINT)wParam;
941 if (uiAction == SPI_SETSCREENSAVETIMEOUT
942 || uiAction == SPI_SETSCREENSAVEACTIVE)
943 {
944 SetEvent(Session->hScreenSaverParametersChanged);
945 }
946 return TRUE;
947 }
948 case WLX_WM_SAS:
949 {
950 DispatchSAS(Session, (DWORD)wParam);
951 return TRUE;
952 }
953 case PM_WINLOGON_EXITWINDOWS:
954 {
955 UINT Flags = (UINT)lParam;
956 UINT Action = Flags & EWX_ACTION_MASK;
957 DWORD wlxAction;
958
959 /* Check parameters */
960 switch (Action)
961 {
962 case EWX_LOGOFF: wlxAction = WLX_SAS_ACTION_LOGOFF; break;
963 case EWX_SHUTDOWN: wlxAction = WLX_SAS_ACTION_SHUTDOWN; break;
964 case EWX_REBOOT: wlxAction = WLX_SAS_ACTION_SHUTDOWN_REBOOT; break;
965 case EWX_POWEROFF: wlxAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; break;
966 default:
967 {
968 ERR("Invalid ExitWindows action 0x%x\n", Action);
969 return STATUS_INVALID_PARAMETER;
970 }
971 }
972
973 if (WLX_SHUTTINGDOWN(wlxAction))
974 {
975 NTSTATUS Status = CheckForShutdownPrivilege((DWORD)wParam);
976 if (!NT_SUCCESS(Status))
977 return Status;
978 }
979 DoGenericAction(Session, wlxAction);
980 return 1;
981 }
982 }
983
984 return DefWindowProc(hwndDlg, uMsg, wParam, lParam);
985 }
986
987 BOOL
988 InitializeSAS(
989 IN OUT PWLSESSION Session)
990 {
991 WNDCLASSEXW swc;
992 BOOL ret = FALSE;
993
994 if (!SwitchDesktop(Session->WinlogonDesktop))
995 {
996 ERR("WL: Failed to switch to winlogon desktop\n");
997 goto cleanup;
998 }
999
1000 /* Register SAS window class */
1001 swc.cbSize = sizeof(WNDCLASSEXW);
1002 swc.style = CS_SAVEBITS;
1003 swc.lpfnWndProc = SASWindowProc;
1004 swc.cbClsExtra = 0;
1005 swc.cbWndExtra = 0;
1006 swc.hInstance = hAppInstance;
1007 swc.hIcon = NULL;
1008 swc.hCursor = NULL;
1009 swc.hbrBackground = NULL;
1010 swc.lpszMenuName = NULL;
1011 swc.lpszClassName = WINLOGON_SAS_CLASS;
1012 swc.hIconSm = NULL;
1013 if (RegisterClassExW(&swc) == 0)
1014 {
1015 ERR("WL: Failed to register SAS window class\n");
1016 goto cleanup;
1017 }
1018
1019 /* Create invisible SAS window */
1020 Session->SASWindow = CreateWindowExW(
1021 0,
1022 WINLOGON_SAS_CLASS,
1023 WINLOGON_SAS_TITLE,
1024 WS_POPUP,
1025 0, 0, 0, 0, 0, 0,
1026 hAppInstance, Session);
1027 if (!Session->SASWindow)
1028 {
1029 ERR("WL: Failed to create SAS window\n");
1030 goto cleanup;
1031 }
1032
1033 /* Register SAS window to receive SAS notifications */
1034 if (!SetLogonNotifyWindow(Session->SASWindow, Session->InteractiveWindowStation))
1035 {
1036 ERR("WL: Failed to register SAS window\n");
1037 goto cleanup;
1038 }
1039
1040 if (!SetDefaultLanguage(FALSE))
1041 return FALSE;
1042
1043 ret = TRUE;
1044
1045 cleanup:
1046 if (!ret)
1047 UninitializeSAS(Session);
1048 return ret;
1049 }