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