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