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