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