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