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