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