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