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