- Sync with trunk r58248 to bring the latest changes from Amine (headers) and others...
[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 WINE_DEFAULT_DEBUG_CHANNEL(winlogon);
17
18 /* GLOBALS ******************************************************************/
19
20 #define WINLOGON_SAS_CLASS L"SAS Window class"
21 #define WINLOGON_SAS_TITLE L"SAS window"
22
23 #define HK_CTRL_ALT_DEL 0
24 #define HK_CTRL_SHIFT_ESC 1
25
26 static BOOL inScrn = FALSE;
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 BOOL
197 PlaySoundRoutine(
198 IN LPCWSTR FileName,
199 IN UINT bLogon,
200 IN UINT Flags)
201 {
202 typedef BOOL (WINAPI *PLAYSOUNDW)(LPCWSTR,HMODULE,DWORD);
203 typedef UINT (WINAPI *WAVEOUTGETNUMDEVS)(VOID);
204 PLAYSOUNDW Play;
205 WAVEOUTGETNUMDEVS waveOutGetNumDevs;
206 UINT NumDevs;
207 HMODULE hLibrary;
208 BOOL Ret = FALSE;
209
210 hLibrary = LoadLibraryW(L"winmm.dll");
211 if (hLibrary)
212 {
213 waveOutGetNumDevs = (WAVEOUTGETNUMDEVS)GetProcAddress(hLibrary, "waveOutGetNumDevs");
214 if (waveOutGetNumDevs)
215 {
216 NumDevs = waveOutGetNumDevs();
217 if (!NumDevs)
218 {
219 if (!bLogon)
220 {
221 Beep(500, 500);
222 }
223 FreeLibrary(hLibrary);
224 return FALSE;
225 }
226 }
227
228 Play = (PLAYSOUNDW)GetProcAddress(hLibrary, "PlaySoundW");
229 if (Play)
230 {
231 Ret = Play(FileName, NULL, Flags);
232 }
233 FreeLibrary(hLibrary);
234 }
235
236 return Ret;
237 }
238
239 DWORD
240 WINAPI
241 PlayLogonSoundThread(
242 IN LPVOID lpParameter)
243 {
244 BYTE TokenUserBuffer[256];
245 PTOKEN_USER pTokenUser = (TOKEN_USER*)TokenUserBuffer;
246 ULONG Length;
247 HKEY hKey;
248 WCHAR wszBuffer[MAX_PATH] = {0};
249 WCHAR wszDest[MAX_PATH];
250 DWORD dwSize = sizeof(wszBuffer), dwType;
251 SERVICE_STATUS_PROCESS Info;
252 UNICODE_STRING SidString;
253 NTSTATUS Status;
254 ULONG Index = 0;
255 SC_HANDLE hSCManager, hService;
256
257 /* Get SID of current user */
258 Status = NtQueryInformationToken((HANDLE)lpParameter,
259 TokenUser,
260 TokenUserBuffer,
261 sizeof(TokenUserBuffer),
262 &Length);
263 if (!NT_SUCCESS(Status))
264 {
265 ERR("NtQueryInformationToken failed: %x!\n", Status);
266 return 0;
267 }
268
269 /* Convert SID to string */
270 RtlInitEmptyUnicodeString(&SidString, wszBuffer, sizeof(wszBuffer));
271 Status = RtlConvertSidToUnicodeString(&SidString, pTokenUser->User.Sid, FALSE);
272 if (!NT_SUCCESS(Status))
273 {
274 ERR("RtlConvertSidToUnicodeString failed: %x!\n", Status);
275 return 0;
276 }
277
278 /* Build path to logon sound registry key.
279 Note: We can't use HKCU here, because Winlogon is owned by SYSTEM user */
280 if (FAILED(StringCbCopyW(wszBuffer + SidString.Length/sizeof(WCHAR),
281 sizeof(wszBuffer) - SidString.Length,
282 L"\\AppEvents\\Schemes\\Apps\\.Default\\WindowsLogon\\.Current")))
283 {
284 /* SID is too long. Should not happen. */
285 ERR("StringCbCopyW failed!\n");
286 return 0;
287 }
288
289 /* Open registry key and query sound path */
290 if (RegOpenKeyExW(HKEY_USERS, wszBuffer, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
291 {
292 ERR("RegOpenKeyExW(%ls) failed!\n", wszBuffer);
293 return 0;
294 }
295
296 if (RegQueryValueExW(hKey, NULL, NULL, &dwType,
297 (LPBYTE)wszBuffer, &dwSize) != ERROR_SUCCESS ||
298 (dwType != REG_SZ && dwType != REG_EXPAND_SZ))
299 {
300 ERR("RegQueryValueExW failed!\n");
301 RegCloseKey(hKey);
302 return 0;
303 }
304
305 RegCloseKey(hKey);
306
307 if (!wszBuffer[0])
308 {
309 /* No sound has been set */
310 ERR("No sound has been set\n");
311 return 0;
312 }
313
314 /* Expand environment variables */
315 if (!ExpandEnvironmentStringsW(wszBuffer, wszDest, MAX_PATH))
316 {
317 ERR("ExpandEnvironmentStringsW failed!\n");
318 return 0;
319 }
320
321 /* Open service manager */
322 hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
323 if (!hSCManager)
324 {
325 ERR("OpenSCManager failed (%x)\n", GetLastError());
326 return 0;
327 }
328
329 /* Open wdmaud service */
330 hService = OpenServiceW(hSCManager, L"wdmaud", GENERIC_READ);
331 if (!hService)
332 {
333 /* Sound is not installed */
334 TRACE("Failed to open wdmaud service (%x)\n", GetLastError());
335 CloseServiceHandle(hSCManager);
336 return 0;
337 }
338
339 /* Wait for wdmaud start */
340 do
341 {
342 if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&Info, sizeof(SERVICE_STATUS_PROCESS), &dwSize))
343 {
344 TRACE("QueryServiceStatusEx failed (%x)\n", GetLastError());
345 break;
346 }
347
348 if (Info.dwCurrentState == SERVICE_RUNNING)
349 break;
350
351 Sleep(1000);
352
353 } while (Index++ < 20);
354
355 CloseServiceHandle(hService);
356 CloseServiceHandle(hSCManager);
357
358 /* If wdmaud is not running exit */
359 if (Info.dwCurrentState != SERVICE_RUNNING)
360 {
361 WARN("wdmaud has not started!\n");
362 return 0;
363 }
364
365 /* Sound subsystem is running. Play logon sound. */
366 TRACE("Playing logon sound: %ls\n", wszDest);
367 PlaySoundRoutine(wszDest, TRUE, SND_FILENAME);
368 return 0;
369 }
370
371 static VOID
372 PlayLogonSound(
373 IN OUT PWLSESSION Session)
374 {
375 HANDLE hThread;
376
377 hThread = CreateThread(NULL, 0, PlayLogonSoundThread, (PVOID)Session->UserToken, 0, NULL);
378 if (hThread)
379 CloseHandle(hThread);
380 }
381
382 static BOOL
383 HandleLogon(
384 IN OUT PWLSESSION Session)
385 {
386 PROFILEINFOW ProfileInfo;
387 BOOL ret = FALSE;
388
389 /* Loading personal settings */
390 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOADINGYOURPERSONALSETTINGS);
391 ProfileInfo.hProfile = INVALID_HANDLE_VALUE;
392 if (0 == (Session->Options & WLX_LOGON_OPT_NO_PROFILE))
393 {
394 if (Session->Profile == NULL
395 || (Session->Profile->dwType != WLX_PROFILE_TYPE_V1_0
396 && Session->Profile->dwType != WLX_PROFILE_TYPE_V2_0))
397 {
398 ERR("WL: Wrong profile\n");
399 goto cleanup;
400 }
401
402 /* Load the user profile */
403 ZeroMemory(&ProfileInfo, sizeof(PROFILEINFOW));
404 ProfileInfo.dwSize = sizeof(PROFILEINFOW);
405 ProfileInfo.dwFlags = 0;
406 ProfileInfo.lpUserName = Session->MprNotifyInfo.pszUserName;
407 ProfileInfo.lpProfilePath = Session->Profile->pszProfile;
408 if (Session->Profile->dwType >= WLX_PROFILE_TYPE_V2_0)
409 {
410 ProfileInfo.lpDefaultPath = Session->Profile->pszNetworkDefaultUserProfile;
411 ProfileInfo.lpServerName = Session->Profile->pszServerName;
412 ProfileInfo.lpPolicyPath = Session->Profile->pszPolicy;
413 }
414
415 if (!LoadUserProfileW(Session->UserToken, &ProfileInfo))
416 {
417 ERR("WL: LoadUserProfileW() failed\n");
418 goto cleanup;
419 }
420 }
421
422 /* Create environment block for the user */
423 if (!CreateUserEnvironment(Session))
424 {
425 WARN("WL: SetUserEnvironment() failed\n");
426 goto cleanup;
427 }
428
429 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_APPLYINGYOURPERSONALSETTINGS);
430 UpdatePerUserSystemParameters(0, TRUE);
431
432 /* Set default language */
433 if (!SetDefaultLanguage(TRUE))
434 {
435 WARN("WL: SetDefaultLanguage() failed\n");
436 goto cleanup;
437 }
438
439 if (!StartUserShell(Session))
440 {
441 //WCHAR StatusMsg[256];
442 WARN("WL: WlxActivateUserShell() failed\n");
443 //LoadStringW(hAppInstance, IDS_FAILEDACTIVATEUSERSHELL, StatusMsg, sizeof(StatusMsg));
444 //MessageBoxW(0, StatusMsg, NULL, MB_ICONERROR);
445 goto cleanup;
446 }
447
448 if (!InitializeScreenSaver(Session))
449 WARN("WL: Failed to initialize screen saver\n");
450
451 Session->hProfileInfo = ProfileInfo.hProfile;
452
453 /* Logon has successed. Play sound. */
454 PlayLogonSound(Session);
455
456 ret = TRUE;
457
458 cleanup:
459 if (Session->Profile)
460 {
461 HeapFree(GetProcessHeap(), 0, Session->Profile->pszProfile);
462 HeapFree(GetProcessHeap(), 0, Session->Profile);
463 }
464 Session->Profile = NULL;
465 if (!ret
466 && ProfileInfo.hProfile != INVALID_HANDLE_VALUE)
467 {
468 UnloadUserProfile(WLSession->UserToken, ProfileInfo.hProfile);
469 }
470 RemoveStatusMessage(Session);
471 if (!ret)
472 {
473 CloseHandle(Session->UserToken);
474 Session->UserToken = NULL;
475 }
476 return ret;
477 }
478
479 #define EWX_ACTION_MASK 0xffffffeb
480 #define EWX_FLAGS_MASK 0x00000014
481
482 typedef struct tagLOGOFF_SHUTDOWN_DATA
483 {
484 UINT Flags;
485 PWLSESSION Session;
486 } LOGOFF_SHUTDOWN_DATA, *PLOGOFF_SHUTDOWN_DATA;
487
488 static DWORD WINAPI
489 LogoffShutdownThread(LPVOID Parameter)
490 {
491 PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA)Parameter;
492
493 if (LSData->Session->UserToken != NULL && !ImpersonateLoggedOnUser(LSData->Session->UserToken))
494 {
495 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
496 return 0;
497 }
498
499 /* Close processes of the interactive user */
500 if (!ExitWindowsEx(
501 EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) |
502 (EWX_LOGOFF == (LSData->Flags & EWX_ACTION_MASK) ? EWX_INTERNAL_FLAG_LOGOFF : 0),
503 0))
504 {
505 ERR("Unable to kill user apps, error %lu\n", GetLastError());
506 RevertToSelf();
507 return 0;
508 }
509
510 /* FIXME: Call ExitWindowsEx() to terminate COM processes */
511
512 if (LSData->Session->UserToken)
513 RevertToSelf();
514
515 return 1;
516 }
517
518
519 static NTSTATUS
520 CreateLogoffSecurityAttributes(
521 OUT PSECURITY_ATTRIBUTES* ppsa)
522 {
523 /* The following code is not working yet and messy */
524 /* Still, it gives some ideas about data types and functions involved and */
525 /* required to set up a SECURITY_DESCRIPTOR for a SECURITY_ATTRIBUTES */
526 /* instance for a thread, to allow that thread to ImpersonateLoggedOnUser(). */
527 /* Specifically THREAD_SET_THREAD_TOKEN is required. */
528 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
529 PSECURITY_ATTRIBUTES psa = 0;
530 BYTE* pMem;
531 PACL pACL;
532 EXPLICIT_ACCESS Access;
533 PSID pEveryoneSID = NULL;
534 static SID_IDENTIFIER_AUTHORITY WorldAuthority = { SECURITY_WORLD_SID_AUTHORITY };
535
536 *ppsa = NULL;
537
538 // Let's first try to enumerate what kind of data we need for this to ever work:
539 // 1. The Winlogon SID, to be able to give it THREAD_SET_THREAD_TOKEN.
540 // 2. The users SID (the user trying to logoff, or rather shut down the system).
541 // 3. At least two EXPLICIT_ACCESS instances:
542 // 3.1 One for Winlogon itself, giving it the rights
543 // required to THREAD_SET_THREAD_TOKEN (as it's needed to successfully call
544 // ImpersonateLoggedOnUser).
545 // 3.2 One for the user, to allow *that* thread to perform its work.
546 // 4. An ACL to hold the these EXPLICIT_ACCESS ACE's.
547 // 5. A SECURITY_DESCRIPTOR to hold the ACL, and finally.
548 // 6. A SECURITY_ATTRIBUTES instance to pull all of this required stuff
549 // together, to hand it to CreateThread.
550 //
551 // However, it seems struct LOGOFF_SHUTDOWN_DATA doesn't contain
552 // these required SID's, why they'd have to be added.
553 // The Winlogon's own SID should probably only be created once,
554 // while the user's SID obviously must be created for each new user.
555 // Might as well store it when the user logs on?
556
557 if(!AllocateAndInitializeSid(&WorldAuthority,
558 1,
559 SECURITY_WORLD_RID,
560 0, 0, 0, 0, 0, 0, 0,
561 &pEveryoneSID))
562 {
563 ERR("Failed to initialize security descriptor for logoff thread!\n");
564 return STATUS_UNSUCCESSFUL;
565 }
566
567 /* set up the required security attributes to be able to shut down */
568 /* To save space and time, allocate a single block of memory holding */
569 /* both SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR */
570 pMem = HeapAlloc(GetProcessHeap(),
571 0,
572 sizeof(SECURITY_ATTRIBUTES) +
573 SECURITY_DESCRIPTOR_MIN_LENGTH +
574 sizeof(ACL));
575 if (!pMem)
576 {
577 ERR("Failed to allocate memory for logoff security descriptor!\n");
578 return STATUS_NO_MEMORY;
579 }
580
581 /* Note that the security descriptor needs to be in _absolute_ format, */
582 /* meaning its members must be pointers to other structures, rather */
583 /* than the relative format using offsets */
584 psa = (PSECURITY_ATTRIBUTES)pMem;
585 SecurityDescriptor = (PSECURITY_DESCRIPTOR)(pMem + sizeof(SECURITY_ATTRIBUTES));
586 pACL = (PACL)(((PBYTE)SecurityDescriptor) + SECURITY_DESCRIPTOR_MIN_LENGTH);
587
588 // Initialize an EXPLICIT_ACCESS structure for an ACE.
589 // The ACE will allow this thread to log off (and shut down the system, currently).
590 ZeroMemory(&Access, sizeof(Access));
591 Access.grfAccessPermissions = THREAD_SET_THREAD_TOKEN;
592 Access.grfAccessMode = SET_ACCESS; // GRANT_ACCESS?
593 Access.grfInheritance = NO_INHERITANCE;
594 Access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
595 Access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
596 Access.Trustee.ptstrName = pEveryoneSID;
597
598 if (SetEntriesInAcl(1, &Access, NULL, &pACL) != ERROR_SUCCESS)
599 {
600 ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n");
601
602 HeapFree(GetProcessHeap(), 0, pMem);
603 return STATUS_UNSUCCESSFUL;
604 }
605
606 if (!InitializeSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
607 {
608 ERR("Failed to initialize security descriptor for logoff thread!\n");
609 HeapFree(GetProcessHeap(), 0, pMem);
610 return STATUS_UNSUCCESSFUL;
611 }
612
613 if (!SetSecurityDescriptorDacl(SecurityDescriptor,
614 TRUE, // bDaclPresent flag
615 pACL,
616 FALSE)) // not a default DACL
617 {
618 ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
619 HeapFree(GetProcessHeap(), 0, pMem);
620 return STATUS_UNSUCCESSFUL;
621 }
622
623 psa->nLength = sizeof(SECURITY_ATTRIBUTES);
624 psa->lpSecurityDescriptor = SecurityDescriptor;
625 psa->bInheritHandle = FALSE;
626
627 *ppsa = psa;
628
629 return STATUS_SUCCESS;
630 }
631
632 static VOID
633 DestroyLogoffSecurityAttributes(
634 IN PSECURITY_ATTRIBUTES psa)
635 {
636 if (psa)
637 {
638 HeapFree(GetProcessHeap(), 0, psa);
639 }
640 }
641
642
643 static NTSTATUS
644 HandleLogoff(
645 IN OUT PWLSESSION Session,
646 IN UINT Flags)
647 {
648 PLOGOFF_SHUTDOWN_DATA LSData;
649 PSECURITY_ATTRIBUTES psa;
650 HANDLE hThread;
651 DWORD exitCode;
652 NTSTATUS Status;
653
654 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_SAVEYOURSETTINGS);
655
656 /* Prepare data for logoff thread */
657 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
658 if (!LSData)
659 {
660 ERR("Failed to allocate mem for thread data\n");
661 return STATUS_NO_MEMORY;
662 }
663 LSData->Flags = Flags;
664 LSData->Session = Session;
665
666 Status = CreateLogoffSecurityAttributes(&psa);
667 if (!NT_SUCCESS(Status))
668 {
669 ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status);
670 HeapFree(GetProcessHeap(), 0, LSData);
671 return Status;
672 }
673
674 /* Run logoff thread */
675 hThread = CreateThread(psa, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
676
677 /* we're done with the SECURITY_DESCRIPTOR */
678 DestroyLogoffSecurityAttributes(psa);
679 psa = NULL;
680
681 if (!hThread)
682 {
683 ERR("Unable to create logoff thread, error %lu\n", GetLastError());
684 HeapFree(GetProcessHeap(), 0, LSData);
685 return STATUS_UNSUCCESSFUL;
686 }
687 WaitForSingleObject(hThread, INFINITE);
688 HeapFree(GetProcessHeap(), 0, LSData);
689 if (!GetExitCodeThread(hThread, &exitCode))
690 {
691 ERR("Unable to get exit code of logoff thread (error %lu)\n", GetLastError());
692 CloseHandle(hThread);
693 return STATUS_UNSUCCESSFUL;
694 }
695 CloseHandle(hThread);
696 if (exitCode == 0)
697 {
698 ERR("Logoff thread returned failure\n");
699 return STATUS_UNSUCCESSFUL;
700 }
701
702 UnloadUserProfile(Session->UserToken, Session->hProfileInfo);
703 CloseHandle(Session->UserToken);
704 UpdatePerUserSystemParameters(0, FALSE);
705 Session->LogonStatus = WKSTA_IS_LOGGED_OFF;
706 Session->UserToken = NULL;
707 return STATUS_SUCCESS;
708 }
709
710 static INT_PTR CALLBACK
711 ShutdownComputerWindowProc(
712 IN HWND hwndDlg,
713 IN UINT uMsg,
714 IN WPARAM wParam,
715 IN LPARAM lParam)
716 {
717 UNREFERENCED_PARAMETER(lParam);
718
719 switch (uMsg)
720 {
721 case WM_COMMAND:
722 {
723 switch (LOWORD(wParam))
724 {
725 case IDC_BTNSHTDOWNCOMPUTER:
726 EndDialog(hwndDlg, IDC_BTNSHTDOWNCOMPUTER);
727 return TRUE;
728 }
729 break;
730 }
731 case WM_INITDIALOG:
732 {
733 RemoveMenu(GetSystemMenu(hwndDlg, FALSE), SC_CLOSE, MF_BYCOMMAND);
734 SetFocus(GetDlgItem(hwndDlg, IDC_BTNSHTDOWNCOMPUTER));
735 return TRUE;
736 }
737 }
738 return FALSE;
739 }
740
741 static VOID
742 UninitializeSAS(
743 IN OUT PWLSESSION Session)
744 {
745 if (Session->SASWindow)
746 {
747 DestroyWindow(Session->SASWindow);
748 Session->SASWindow = NULL;
749 }
750 if (Session->hEndOfScreenSaverThread)
751 SetEvent(Session->hEndOfScreenSaverThread);
752 UnregisterClassW(WINLOGON_SAS_CLASS, hAppInstance);
753 }
754
755 NTSTATUS
756 HandleShutdown(
757 IN OUT PWLSESSION Session,
758 IN DWORD wlxAction)
759 {
760 PLOGOFF_SHUTDOWN_DATA LSData;
761 HANDLE hThread;
762 DWORD exitCode;
763
764 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_REACTOSISSHUTTINGDOWN);
765
766 /* Prepare data for shutdown thread */
767 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
768 if (!LSData)
769 {
770 ERR("Failed to allocate mem for thread data\n");
771 return STATUS_NO_MEMORY;
772 }
773 if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF)
774 LSData->Flags = EWX_POWEROFF;
775 else if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
776 LSData->Flags = EWX_REBOOT;
777 else
778 LSData->Flags = EWX_SHUTDOWN;
779 LSData->Session = Session;
780
781 /* Run shutdown thread */
782 hThread = CreateThread(NULL, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
783 if (!hThread)
784 {
785 ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
786 HeapFree(GetProcessHeap(), 0, LSData);
787 return STATUS_UNSUCCESSFUL;
788 }
789 WaitForSingleObject(hThread, INFINITE);
790 HeapFree(GetProcessHeap(), 0, LSData);
791 if (!GetExitCodeThread(hThread, &exitCode))
792 {
793 ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError());
794 CloseHandle(hThread);
795 return STATUS_UNSUCCESSFUL;
796 }
797 CloseHandle(hThread);
798 if (exitCode == 0)
799 {
800 ERR("Shutdown thread returned failure\n");
801 return STATUS_UNSUCCESSFUL;
802 }
803
804 /* Destroy SAS window */
805 UninitializeSAS(Session);
806
807 FIXME("FIXME: Call SMSS API #1\n");
808 if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT)
809 NtShutdownSystem(ShutdownReboot);
810 else
811 {
812 if (FALSE)
813 {
814 /* FIXME - only show this dialog if it's a shutdown and the computer doesn't support APM */
815 DialogBox(hAppInstance, MAKEINTRESOURCE(IDD_SHUTDOWNCOMPUTER), GetDesktopWindow(), ShutdownComputerWindowProc);
816 }
817 NtShutdownSystem(ShutdownNoReboot);
818 }
819 return STATUS_SUCCESS;
820 }
821
822 static VOID
823 DoGenericAction(
824 IN OUT PWLSESSION Session,
825 IN DWORD wlxAction)
826 {
827 switch (wlxAction)
828 {
829 case WLX_SAS_ACTION_LOGON: /* 0x01 */
830 if (HandleLogon(Session))
831 {
832 SwitchDesktop(Session->ApplicationDesktop);
833 Session->LogonStatus = WKSTA_IS_LOGGED_ON;
834 }
835 else
836 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
837 break;
838 case WLX_SAS_ACTION_NONE: /* 0x02 */
839 break;
840 case WLX_SAS_ACTION_LOCK_WKSTA: /* 0x03 */
841 if (Session->Gina.Functions.WlxIsLockOk(Session->Gina.Context))
842 {
843 SwitchDesktop(WLSession->WinlogonDesktop);
844 Session->LogonStatus = WKSTA_IS_LOCKED;
845 Session->Gina.Functions.WlxDisplayLockedNotice(Session->Gina.Context);
846 }
847 break;
848 case WLX_SAS_ACTION_LOGOFF: /* 0x04 */
849 case WLX_SAS_ACTION_SHUTDOWN: /* 0x05 */
850 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF: /* 0x0a */
851 case WLX_SAS_ACTION_SHUTDOWN_REBOOT: /* 0x0b */
852 if (Session->LogonStatus != WKSTA_IS_LOGGED_OFF)
853 {
854 if (!Session->Gina.Functions.WlxIsLogoffOk(Session->Gina.Context))
855 break;
856 SwitchDesktop(WLSession->WinlogonDesktop);
857 Session->Gina.Functions.WlxLogoff(Session->Gina.Context);
858 if (!NT_SUCCESS(HandleLogoff(Session, EWX_LOGOFF)))
859 {
860 RemoveStatusMessage(Session);
861 break;
862 }
863 }
864 if (WLX_SHUTTINGDOWN(wlxAction))
865 {
866 Session->Gina.Functions.WlxShutdown(Session->Gina.Context, wlxAction);
867 if (!NT_SUCCESS(HandleShutdown(Session, wlxAction)))
868 {
869 RemoveStatusMessage(Session);
870 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
871 }
872 }
873 else
874 {
875 RemoveStatusMessage(Session);
876 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
877 }
878 break;
879 case WLX_SAS_ACTION_TASKLIST: /* 0x07 */
880 SwitchDesktop(WLSession->ApplicationDesktop);
881 StartTaskManager(Session);
882 break;
883 case WLX_SAS_ACTION_UNLOCK_WKSTA: /* 0x08 */
884 SwitchDesktop(WLSession->ApplicationDesktop);
885 Session->LogonStatus = WKSTA_IS_LOGGED_ON;
886 break;
887 default:
888 WARN("Unknown SAS action 0x%lx\n", wlxAction);
889 }
890 }
891
892 static VOID
893 DispatchSAS(
894 IN OUT PWLSESSION Session,
895 IN DWORD dwSasType)
896 {
897 DWORD wlxAction = WLX_SAS_ACTION_NONE;
898
899 if (Session->LogonStatus == WKSTA_IS_LOGGED_ON)
900 wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOnSAS(Session->Gina.Context, dwSasType, NULL);
901 else if (Session->LogonStatus == WKSTA_IS_LOCKED)
902 wlxAction = (DWORD)Session->Gina.Functions.WlxWkstaLockedSAS(Session->Gina.Context, dwSasType);
903 else
904 {
905 /* Display a new dialog (if necessary) */
906 switch (dwSasType)
907 {
908 case WLX_SAS_TYPE_TIMEOUT: /* 0x00 */
909 {
910 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
911 break;
912 }
913 default:
914 {
915 PSID LogonSid = NULL; /* FIXME */
916
917 Session->Options = 0;
918
919 wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOutSAS(
920 Session->Gina.Context,
921 Session->SASAction,
922 &Session->LogonId,
923 LogonSid,
924 &Session->Options,
925 &Session->UserToken,
926 &Session->MprNotifyInfo,
927 (PVOID*)&Session->Profile);
928 break;
929 }
930 }
931 }
932
933 if (dwSasType == WLX_SAS_TYPE_SCRNSVR_TIMEOUT)
934 {
935 BOOL bSecure = TRUE;
936 if (!Session->Gina.Functions.WlxScreenSaverNotify(Session->Gina.Context, &bSecure))
937 {
938 /* Skip start of screen saver */
939 SetEvent(Session->hEndOfScreenSaver);
940 }
941 else
942 {
943 if (bSecure)
944 DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
945 StartScreenSaver(Session);
946 }
947 }
948 else if (dwSasType == WLX_SAS_TYPE_SCRNSVR_ACTIVITY)
949 SetEvent(Session->hUserActivity);
950
951 DoGenericAction(Session, wlxAction);
952 }
953
954 static BOOL
955 RegisterHotKeys(
956 IN PWLSESSION Session,
957 IN HWND hwndSAS)
958 {
959 /* Register Ctrl+Alt+Del Hotkey */
960 if (!RegisterHotKey(hwndSAS, HK_CTRL_ALT_DEL, MOD_CONTROL | MOD_ALT, VK_DELETE))
961 {
962 ERR("WL: Unable to register Ctrl+Alt+Del hotkey!\n");
963 return FALSE;
964 }
965
966 /* Register Ctrl+Shift+Esc (optional) */
967 Session->TaskManHotkey = RegisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC, MOD_CONTROL | MOD_SHIFT, VK_ESCAPE);
968 if (!Session->TaskManHotkey)
969 WARN("WL: Warning: Unable to register Ctrl+Alt+Esc hotkey!\n");
970 return TRUE;
971 }
972
973 static BOOL
974 UnregisterHotKeys(
975 IN PWLSESSION Session,
976 IN HWND hwndSAS)
977 {
978 /* Unregister hotkeys */
979 UnregisterHotKey(hwndSAS, HK_CTRL_ALT_DEL);
980
981 if (Session->TaskManHotkey)
982 UnregisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC);
983
984 return TRUE;
985 }
986
987 static NTSTATUS
988 CheckForShutdownPrivilege(
989 IN DWORD RequestingProcessId)
990 {
991 HANDLE Process;
992 HANDLE Token;
993 BOOL CheckResult;
994 PPRIVILEGE_SET PrivSet;
995
996 TRACE("CheckForShutdownPrivilege()\n");
997
998 Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, RequestingProcessId);
999 if (!Process)
1000 {
1001 WARN("OpenProcess() failed with error %lu\n", GetLastError());
1002 return STATUS_INVALID_HANDLE;
1003 }
1004 if (!OpenProcessToken(Process, TOKEN_QUERY, &Token))
1005 {
1006 WARN("OpenProcessToken() failed with error %lu\n", GetLastError());
1007 CloseHandle(Process);
1008 return STATUS_INVALID_HANDLE;
1009 }
1010 CloseHandle(Process);
1011 PrivSet = HeapAlloc(GetProcessHeap(), 0, sizeof(PRIVILEGE_SET) + sizeof(LUID_AND_ATTRIBUTES));
1012 if (!PrivSet)
1013 {
1014 ERR("Failed to allocate mem for privilege set\n");
1015 CloseHandle(Token);
1016 return STATUS_NO_MEMORY;
1017 }
1018 PrivSet->PrivilegeCount = 1;
1019 PrivSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
1020 if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &PrivSet->Privilege[0].Luid))
1021 {
1022 WARN("LookupPrivilegeValue() failed with error %lu\n", GetLastError());
1023 HeapFree(GetProcessHeap(), 0, PrivSet);
1024 CloseHandle(Token);
1025 return STATUS_UNSUCCESSFUL;
1026 }
1027 if (!PrivilegeCheck(Token, PrivSet, &CheckResult))
1028 {
1029 WARN("PrivilegeCheck() failed with error %lu\n", GetLastError());
1030 HeapFree(GetProcessHeap(), 0, PrivSet);
1031 CloseHandle(Token);
1032 return STATUS_ACCESS_DENIED;
1033 }
1034 HeapFree(GetProcessHeap(), 0, PrivSet);
1035 CloseHandle(Token);
1036
1037 if (!CheckResult)
1038 {
1039 WARN("SE_SHUTDOWN privilege not enabled\n");
1040 return STATUS_ACCESS_DENIED;
1041 }
1042 return STATUS_SUCCESS;
1043 }
1044
1045 BOOL
1046 WINAPI
1047 HandleMessageBeep(UINT uType)
1048 {
1049 LPWSTR EventName;
1050
1051 switch(uType)
1052 {
1053 case 0xFFFFFFFF:
1054 EventName = NULL;
1055 break;
1056 case MB_OK:
1057 EventName = L"SystemDefault";
1058 break;
1059 case MB_ICONASTERISK:
1060 EventName = L"SystemAsterisk";
1061 break;
1062 case MB_ICONEXCLAMATION:
1063 EventName = L"SystemExclamation";
1064 break;
1065 case MB_ICONHAND:
1066 EventName = L"SystemHand";
1067 break;
1068 case MB_ICONQUESTION:
1069 EventName = L"SystemQuestion";
1070 break;
1071 default:
1072 WARN("Unhandled type %d\n", uType);
1073 EventName = L"SystemDefault";
1074 }
1075
1076 return PlaySoundRoutine(EventName, FALSE, SND_ALIAS | SND_NOWAIT | SND_NOSTOP | SND_ASYNC);
1077 }
1078
1079 static LRESULT CALLBACK
1080 SASWindowProc(
1081 IN HWND hwndDlg,
1082 IN UINT uMsg,
1083 IN WPARAM wParam,
1084 IN LPARAM lParam)
1085 {
1086 PWLSESSION Session = (PWLSESSION)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1087
1088 switch (uMsg)
1089 {
1090 case WM_HOTKEY:
1091 {
1092 switch (lParam)
1093 {
1094 case MAKELONG(MOD_CONTROL | MOD_ALT, VK_DELETE):
1095 {
1096 TRACE("SAS: CONTROL+ALT+DELETE\n");
1097 if (!Session->Gina.UseCtrlAltDelete)
1098 break;
1099 PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_CTRL_ALT_DEL, 0);
1100 return TRUE;
1101 }
1102 case MAKELONG(MOD_CONTROL | MOD_SHIFT, VK_ESCAPE):
1103 {
1104 TRACE("SAS: CONTROL+SHIFT+ESCAPE\n");
1105 DoGenericAction(Session, WLX_SAS_ACTION_TASKLIST);
1106 return TRUE;
1107 }
1108 }
1109 break;
1110 }
1111 case WM_CREATE:
1112 {
1113 /* Get the session pointer from the create data */
1114 Session = (PWLSESSION)((LPCREATESTRUCT)lParam)->lpCreateParams;
1115
1116 /* Save the Session pointer */
1117 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)Session);
1118 if (GetSetupType())
1119 return TRUE;
1120 return RegisterHotKeys(Session, hwndDlg);
1121 }
1122 case WM_DESTROY:
1123 {
1124 if (!GetSetupType())
1125 UnregisterHotKeys(Session, hwndDlg);
1126 return TRUE;
1127 }
1128 case WM_SETTINGCHANGE:
1129 {
1130 UINT uiAction = (UINT)wParam;
1131 if (uiAction == SPI_SETSCREENSAVETIMEOUT
1132 || uiAction == SPI_SETSCREENSAVEACTIVE)
1133 {
1134 SetEvent(Session->hScreenSaverParametersChanged);
1135 }
1136 return TRUE;
1137 }
1138 case WM_LOGONNOTIFY:
1139 {
1140 switch(wParam)
1141 {
1142 case LN_MESSAGE_BEEP:
1143 {
1144 return HandleMessageBeep(lParam);
1145 }
1146 case LN_SHELL_EXITED:
1147 {
1148 /* lParam is the exit code */
1149 if(lParam != 1)
1150 {
1151 SetTimer(hwndDlg, 1, 1000, NULL);
1152 }
1153 break;
1154 }
1155 case LN_START_SCREENSAVE:
1156 {
1157 BOOL bSecure = FALSE;
1158
1159 if (inScrn)
1160 break;
1161
1162 inScrn = TRUE;
1163
1164 // lParam 1 == Secure
1165 if (lParam)
1166 {
1167 if (Session->Gina.Functions.WlxScreenSaverNotify(Session->Gina.Context, &bSecure))
1168 {
1169 if (bSecure) DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA);
1170 }
1171 }
1172
1173 StartScreenSaver(Session);
1174 inScrn = FALSE;
1175 break;
1176 }
1177 default:
1178 {
1179 ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam);
1180 }
1181 }
1182 return 0;
1183 }
1184 case WM_TIMER:
1185 {
1186 if (wParam == 1)
1187 {
1188 KillTimer(hwndDlg, 1);
1189 StartUserShell(Session);
1190 }
1191 break;
1192 }
1193 case WLX_WM_SAS:
1194 {
1195 DispatchSAS(Session, (DWORD)wParam);
1196 return TRUE;
1197 }
1198 case PM_WINLOGON_EXITWINDOWS:
1199 {
1200 UINT Flags = (UINT)lParam;
1201 UINT Action = Flags & EWX_ACTION_MASK;
1202 DWORD wlxAction;
1203
1204 /* Check parameters */
1205 switch (Action)
1206 {
1207 case EWX_LOGOFF: wlxAction = WLX_SAS_ACTION_LOGOFF; break;
1208 case EWX_SHUTDOWN: wlxAction = WLX_SAS_ACTION_SHUTDOWN; break;
1209 case EWX_REBOOT: wlxAction = WLX_SAS_ACTION_SHUTDOWN_REBOOT; break;
1210 case EWX_POWEROFF: wlxAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; break;
1211 default:
1212 {
1213 ERR("Invalid ExitWindows action 0x%x\n", Action);
1214 return STATUS_INVALID_PARAMETER;
1215 }
1216 }
1217
1218 if (WLX_SHUTTINGDOWN(wlxAction))
1219 {
1220 NTSTATUS Status = CheckForShutdownPrivilege((DWORD)wParam);
1221 if (!NT_SUCCESS(Status))
1222 return Status;
1223 }
1224 DoGenericAction(Session, wlxAction);
1225 return 1;
1226 }
1227 }
1228
1229 return DefWindowProc(hwndDlg, uMsg, wParam, lParam);
1230 }
1231
1232 BOOL
1233 InitializeSAS(
1234 IN OUT PWLSESSION Session)
1235 {
1236 WNDCLASSEXW swc;
1237 BOOL ret = FALSE;
1238
1239 if (!SwitchDesktop(Session->WinlogonDesktop))
1240 {
1241 ERR("WL: Failed to switch to winlogon desktop\n");
1242 goto cleanup;
1243 }
1244
1245 /* Register SAS window class */
1246 swc.cbSize = sizeof(WNDCLASSEXW);
1247 swc.style = CS_SAVEBITS;
1248 swc.lpfnWndProc = SASWindowProc;
1249 swc.cbClsExtra = 0;
1250 swc.cbWndExtra = 0;
1251 swc.hInstance = hAppInstance;
1252 swc.hIcon = NULL;
1253 swc.hCursor = NULL;
1254 swc.hbrBackground = NULL;
1255 swc.lpszMenuName = NULL;
1256 swc.lpszClassName = WINLOGON_SAS_CLASS;
1257 swc.hIconSm = NULL;
1258 if (RegisterClassExW(&swc) == 0)
1259 {
1260 ERR("WL: Failed to register SAS window class\n");
1261 goto cleanup;
1262 }
1263
1264 /* Create invisible SAS window */
1265 Session->SASWindow = CreateWindowExW(
1266 0,
1267 WINLOGON_SAS_CLASS,
1268 WINLOGON_SAS_TITLE,
1269 WS_POPUP,
1270 0, 0, 0, 0, 0, 0,
1271 hAppInstance, Session);
1272 if (!Session->SASWindow)
1273 {
1274 ERR("WL: Failed to create SAS window\n");
1275 goto cleanup;
1276 }
1277
1278 /* Register SAS window to receive SAS notifications */
1279 if (!SetLogonNotifyWindow(Session->SASWindow, Session->InteractiveWindowStation))
1280 {
1281 ERR("WL: Failed to register SAS window\n");
1282 goto cleanup;
1283 }
1284
1285 if (!SetDefaultLanguage(FALSE))
1286 return FALSE;
1287
1288 ret = TRUE;
1289
1290 cleanup:
1291 if (!ret)
1292 UninitializeSAS(Session);
1293 return ret;
1294 }