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