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