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 VOID 513 RestoreAllConnections(PWLSESSION Session) 514 { 515 DWORD dRet; 516 HANDLE hEnum; 517 LPNETRESOURCE lpRes; 518 DWORD dSize = 0x1000; 519 DWORD dCount = -1; 520 LPNETRESOURCE lpCur; 521 BOOL UserProfile; 522 523 UserProfile = (Session && Session->UserToken); 524 if (!UserProfile) 525 { 526 return; 527 } 528 529 if (!ImpersonateLoggedOnUser(Session->UserToken)) 530 { 531 ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError()); 532 return; 533 } 534 535 dRet = WNetOpenEnum(RESOURCE_REMEMBERED, RESOURCETYPE_DISK, 0, NULL, &hEnum); 536 if (dRet != WN_SUCCESS) 537 { 538 ERR("Failed to open enumeration: %lu\n", dRet); 539 goto quit; 540 } 541 542 lpRes = HeapAlloc(GetProcessHeap(), 0, dSize); 543 if (!lpRes) 544 { 545 ERR("Failed to allocate memory\n"); 546 WNetCloseEnum(hEnum); 547 goto quit; 548 } 549 550 do 551 { 552 dSize = 0x1000; 553 dCount = -1; 554 555 memset(lpRes, 0, dSize); 556 dRet = WNetEnumResource(hEnum, &dCount, lpRes, &dSize); 557 if (dRet == WN_SUCCESS || dRet == WN_MORE_DATA) 558 { 559 lpCur = lpRes; 560 for (; dCount; dCount--) 561 { 562 WNetAddConnection(lpCur->lpRemoteName, NULL, lpCur->lpLocalName); 563 lpCur++; 564 } 565 } 566 } while (dRet != WN_NO_MORE_ENTRIES); 567 568 HeapFree(GetProcessHeap(), 0, lpRes); 569 WNetCloseEnum(hEnum); 570 571 quit: 572 RevertToSelf(); 573 } 574 575 static 576 BOOL 577 HandleLogon( 578 IN OUT PWLSESSION Session) 579 { 580 PROFILEINFOW ProfileInfo; 581 BOOL ret = FALSE; 582 583 /* Loading personal settings */ 584 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOADINGYOURPERSONALSETTINGS); 585 ProfileInfo.hProfile = INVALID_HANDLE_VALUE; 586 if (0 == (Session->Options & WLX_LOGON_OPT_NO_PROFILE)) 587 { 588 if (Session->Profile == NULL 589 || (Session->Profile->dwType != WLX_PROFILE_TYPE_V1_0 590 && Session->Profile->dwType != WLX_PROFILE_TYPE_V2_0)) 591 { 592 ERR("WL: Wrong profile\n"); 593 goto cleanup; 594 } 595 596 /* Load the user profile */ 597 ZeroMemory(&ProfileInfo, sizeof(PROFILEINFOW)); 598 ProfileInfo.dwSize = sizeof(PROFILEINFOW); 599 ProfileInfo.dwFlags = 0; 600 ProfileInfo.lpUserName = Session->MprNotifyInfo.pszUserName; 601 ProfileInfo.lpProfilePath = Session->Profile->pszProfile; 602 if (Session->Profile->dwType >= WLX_PROFILE_TYPE_V2_0) 603 { 604 ProfileInfo.lpDefaultPath = Session->Profile->pszNetworkDefaultUserProfile; 605 ProfileInfo.lpServerName = Session->Profile->pszServerName; 606 ProfileInfo.lpPolicyPath = Session->Profile->pszPolicy; 607 } 608 609 if (!LoadUserProfileW(Session->UserToken, &ProfileInfo)) 610 { 611 ERR("WL: LoadUserProfileW() failed\n"); 612 goto cleanup; 613 } 614 } 615 616 /* Create environment block for the user */ 617 if (!CreateUserEnvironment(Session)) 618 { 619 WARN("WL: SetUserEnvironment() failed\n"); 620 goto cleanup; 621 } 622 623 CallNotificationDlls(Session, LogonHandler); 624 625 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_APPLYINGYOURPERSONALSETTINGS); 626 UpdatePerUserSystemParameters(0, TRUE); 627 628 /* Set default user language */ 629 if (!SetDefaultLanguage(Session)) 630 { 631 WARN("WL: SetDefaultLanguage() failed\n"); 632 goto cleanup; 633 } 634 635 AllowWinstaAccess(Session); 636 637 /* Connect remote resources */ 638 RestoreAllConnections(Session); 639 640 if (!StartUserShell(Session)) 641 { 642 //WCHAR StatusMsg[256]; 643 WARN("WL: WlxActivateUserShell() failed\n"); 644 //LoadStringW(hAppInstance, IDS_FAILEDACTIVATEUSERSHELL, StatusMsg, sizeof(StatusMsg) / sizeof(StatusMsg[0])); 645 //MessageBoxW(0, StatusMsg, NULL, MB_ICONERROR); 646 goto cleanup; 647 } 648 649 CallNotificationDlls(Session, StartShellHandler); 650 651 if (!InitializeScreenSaver(Session)) 652 WARN("WL: Failed to initialize screen saver\n"); 653 654 Session->hProfileInfo = ProfileInfo.hProfile; 655 656 /* Logon has succeeded. Play sound. */ 657 PlayLogonSound(Session); 658 659 ret = TRUE; 660 661 cleanup: 662 if (Session->Profile) 663 { 664 HeapFree(GetProcessHeap(), 0, Session->Profile->pszProfile); 665 HeapFree(GetProcessHeap(), 0, Session->Profile); 666 } 667 Session->Profile = NULL; 668 if (!ret && ProfileInfo.hProfile != INVALID_HANDLE_VALUE) 669 { 670 UnloadUserProfile(Session->UserToken, ProfileInfo.hProfile); 671 } 672 RemoveStatusMessage(Session); 673 if (!ret) 674 { 675 SetWindowStationUser(Session->InteractiveWindowStation, 676 &LuidNone, NULL, 0); 677 CloseHandle(Session->UserToken); 678 Session->UserToken = NULL; 679 } 680 681 if (ret) 682 { 683 SwitchDesktop(Session->ApplicationDesktop); 684 Session->LogonState = STATE_LOGGED_ON; 685 } 686 687 return ret; 688 } 689 690 691 static 692 DWORD 693 WINAPI 694 LogoffShutdownThread( 695 LPVOID Parameter) 696 { 697 DWORD ret = 1; 698 PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA)Parameter; 699 UINT uFlags; 700 701 if (LSData->Session->UserToken != NULL && 702 !ImpersonateLoggedOnUser(LSData->Session->UserToken)) 703 { 704 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError()); 705 return 0; 706 } 707 708 // FIXME: To be really fixed: need to check what needs to be kept and what needs to be removed there. 709 // 710 // uFlags = EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) | 711 // ((LSData->Flags & EWX_ACTION_MASK) == EWX_LOGOFF ? EWX_CALLER_WINLOGON_LOGOFF : 0); 712 713 uFlags = EWX_CALLER_WINLOGON | (LSData->Flags & 0x0F); 714 715 TRACE("In LogoffShutdownThread with uFlags == 0x%x; exit_in_progress == %s\n", 716 uFlags, ExitReactOSInProgress ? "true" : "false"); 717 718 ExitReactOSInProgress = TRUE; 719 720 /* Close processes of the interactive user */ 721 if (!ExitWindowsEx(uFlags, 0)) 722 { 723 ERR("Unable to kill user apps, error %lu\n", GetLastError()); 724 ret = 0; 725 } 726 727 /* Cancel all the user connections */ 728 WNetClearConnections(0); 729 730 if (LSData->Session->UserToken) 731 RevertToSelf(); 732 733 return ret; 734 } 735 736 static 737 DWORD 738 WINAPI 739 KillComProcesses( 740 LPVOID Parameter) 741 { 742 DWORD ret = 1; 743 PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA)Parameter; 744 745 TRACE("In KillComProcesses\n"); 746 747 if (LSData->Session->UserToken != NULL && 748 !ImpersonateLoggedOnUser(LSData->Session->UserToken)) 749 { 750 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError()); 751 return 0; 752 } 753 754 /* Attempt to kill remaining processes. No notifications needed. */ 755 if (!ExitWindowsEx(EWX_CALLER_WINLOGON | EWX_NONOTIFY | EWX_FORCE | EWX_LOGOFF, 0)) 756 { 757 ERR("Unable to kill COM apps, error %lu\n", GetLastError()); 758 ret = 0; 759 } 760 761 if (LSData->Session->UserToken) 762 RevertToSelf(); 763 764 return ret; 765 } 766 767 static 768 NTSTATUS 769 CreateLogoffSecurityAttributes( 770 OUT PSECURITY_ATTRIBUTES* ppsa) 771 { 772 /* The following code is not working yet and messy */ 773 /* Still, it gives some ideas about data types and functions involved and */ 774 /* required to set up a SECURITY_DESCRIPTOR for a SECURITY_ATTRIBUTES */ 775 /* instance for a thread, to allow that thread to ImpersonateLoggedOnUser(). */ 776 /* Specifically THREAD_SET_THREAD_TOKEN is required. */ 777 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; 778 PSECURITY_ATTRIBUTES psa = 0; 779 BYTE* pMem; 780 PACL pACL; 781 EXPLICIT_ACCESS Access; 782 PSID pEveryoneSID = NULL; 783 static SID_IDENTIFIER_AUTHORITY WorldAuthority = { SECURITY_WORLD_SID_AUTHORITY }; 784 785 *ppsa = NULL; 786 787 // Let's first try to enumerate what kind of data we need for this to ever work: 788 // 1. The Winlogon SID, to be able to give it THREAD_SET_THREAD_TOKEN. 789 // 2. The users SID (the user trying to logoff, or rather shut down the system). 790 // 3. At least two EXPLICIT_ACCESS instances: 791 // 3.1 One for Winlogon itself, giving it the rights 792 // required to THREAD_SET_THREAD_TOKEN (as it's needed to successfully call 793 // ImpersonateLoggedOnUser). 794 // 3.2 One for the user, to allow *that* thread to perform its work. 795 // 4. An ACL to hold the these EXPLICIT_ACCESS ACE's. 796 // 5. A SECURITY_DESCRIPTOR to hold the ACL, and finally. 797 // 6. A SECURITY_ATTRIBUTES instance to pull all of this required stuff 798 // together, to hand it to CreateThread. 799 // 800 // However, it seems struct LOGOFF_SHUTDOWN_DATA doesn't contain 801 // these required SID's, why they'd have to be added. 802 // The Winlogon's own SID should probably only be created once, 803 // while the user's SID obviously must be created for each new user. 804 // Might as well store it when the user logs on? 805 806 if(!AllocateAndInitializeSid(&WorldAuthority, 807 1, 808 SECURITY_WORLD_RID, 809 0, 0, 0, 0, 0, 0, 0, 810 &pEveryoneSID)) 811 { 812 ERR("Failed to initialize security descriptor for logoff thread!\n"); 813 return STATUS_UNSUCCESSFUL; 814 } 815 816 /* set up the required security attributes to be able to shut down */ 817 /* To save space and time, allocate a single block of memory holding */ 818 /* both SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR */ 819 pMem = HeapAlloc(GetProcessHeap(), 820 0, 821 sizeof(SECURITY_ATTRIBUTES) + 822 SECURITY_DESCRIPTOR_MIN_LENGTH + 823 sizeof(ACL)); 824 if (!pMem) 825 { 826 ERR("Failed to allocate memory for logoff security descriptor!\n"); 827 return STATUS_NO_MEMORY; 828 } 829 830 /* Note that the security descriptor needs to be in _absolute_ format, */ 831 /* meaning its members must be pointers to other structures, rather */ 832 /* than the relative format using offsets */ 833 psa = (PSECURITY_ATTRIBUTES)pMem; 834 SecurityDescriptor = (PSECURITY_DESCRIPTOR)(pMem + sizeof(SECURITY_ATTRIBUTES)); 835 pACL = (PACL)(((PBYTE)SecurityDescriptor) + SECURITY_DESCRIPTOR_MIN_LENGTH); 836 837 // Initialize an EXPLICIT_ACCESS structure for an ACE. 838 // The ACE will allow this thread to log off (and shut down the system, currently). 839 ZeroMemory(&Access, sizeof(Access)); 840 Access.grfAccessPermissions = THREAD_SET_THREAD_TOKEN; 841 Access.grfAccessMode = SET_ACCESS; // GRANT_ACCESS? 842 Access.grfInheritance = NO_INHERITANCE; 843 Access.Trustee.TrusteeForm = TRUSTEE_IS_SID; 844 Access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; 845 Access.Trustee.ptstrName = pEveryoneSID; 846 847 if (SetEntriesInAcl(1, &Access, NULL, &pACL) != ERROR_SUCCESS) 848 { 849 ERR("Failed to set Access Rights for logoff thread. Logging out will most likely fail.\n"); 850 851 HeapFree(GetProcessHeap(), 0, pMem); 852 return STATUS_UNSUCCESSFUL; 853 } 854 855 if (!InitializeSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION)) 856 { 857 ERR("Failed to initialize security descriptor for logoff thread!\n"); 858 HeapFree(GetProcessHeap(), 0, pMem); 859 return STATUS_UNSUCCESSFUL; 860 } 861 862 if (!SetSecurityDescriptorDacl(SecurityDescriptor, 863 TRUE, // bDaclPresent flag 864 pACL, 865 FALSE)) // not a default DACL 866 { 867 ERR("SetSecurityDescriptorDacl Error %lu\n", GetLastError()); 868 HeapFree(GetProcessHeap(), 0, pMem); 869 return STATUS_UNSUCCESSFUL; 870 } 871 872 psa->nLength = sizeof(SECURITY_ATTRIBUTES); 873 psa->lpSecurityDescriptor = SecurityDescriptor; 874 psa->bInheritHandle = FALSE; 875 876 *ppsa = psa; 877 878 return STATUS_SUCCESS; 879 } 880 881 static 882 VOID 883 DestroyLogoffSecurityAttributes( 884 IN PSECURITY_ATTRIBUTES psa) 885 { 886 if (psa) 887 { 888 HeapFree(GetProcessHeap(), 0, psa); 889 } 890 } 891 892 893 static 894 NTSTATUS 895 HandleLogoff( 896 IN OUT PWLSESSION Session, 897 IN UINT Flags) 898 { 899 PLOGOFF_SHUTDOWN_DATA LSData; 900 PSECURITY_ATTRIBUTES psa; 901 HANDLE hThread; 902 DWORD exitCode; 903 NTSTATUS Status; 904 905 /* Prepare data for logoff thread */ 906 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA)); 907 if (!LSData) 908 { 909 ERR("Failed to allocate mem for thread data\n"); 910 return STATUS_NO_MEMORY; 911 } 912 LSData->Flags = Flags; 913 LSData->Session = Session; 914 915 Status = CreateLogoffSecurityAttributes(&psa); 916 if (!NT_SUCCESS(Status)) 917 { 918 ERR("Failed to create a required security descriptor. Status 0x%08lx\n", Status); 919 HeapFree(GetProcessHeap(), 0, LSData); 920 return Status; 921 } 922 923 /* Run logoff thread */ 924 hThread = CreateThread(psa, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL); 925 if (!hThread) 926 { 927 ERR("Unable to create logoff thread, error %lu\n", GetLastError()); 928 DestroyLogoffSecurityAttributes(psa); 929 HeapFree(GetProcessHeap(), 0, LSData); 930 return STATUS_UNSUCCESSFUL; 931 } 932 WaitForSingleObject(hThread, INFINITE); 933 if (!GetExitCodeThread(hThread, &exitCode)) 934 { 935 ERR("Unable to get exit code of logoff thread (error %lu)\n", GetLastError()); 936 CloseHandle(hThread); 937 DestroyLogoffSecurityAttributes(psa); 938 HeapFree(GetProcessHeap(), 0, LSData); 939 return STATUS_UNSUCCESSFUL; 940 } 941 CloseHandle(hThread); 942 if (exitCode == 0) 943 { 944 ERR("Logoff thread returned failure\n"); 945 DestroyLogoffSecurityAttributes(psa); 946 HeapFree(GetProcessHeap(), 0, LSData); 947 return STATUS_UNSUCCESSFUL; 948 } 949 950 SwitchDesktop(Session->WinlogonDesktop); 951 952 // TODO: Play logoff sound! 953 954 SetWindowStationUser(Session->InteractiveWindowStation, 955 &LuidNone, NULL, 0); 956 957 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_LOGGINGOFF); 958 959 // FIXME: Closing network connections! 960 // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_CLOSINGNETWORKCONNECTIONS); 961 962 /* Kill remaining COM apps. Only at logoff! */ 963 hThread = CreateThread(psa, 0, KillComProcesses, (LPVOID)LSData, 0, NULL); 964 if (hThread) 965 { 966 WaitForSingleObject(hThread, INFINITE); 967 CloseHandle(hThread); 968 } 969 970 /* We're done with the SECURITY_DESCRIPTOR */ 971 DestroyLogoffSecurityAttributes(psa); 972 psa = NULL; 973 974 HeapFree(GetProcessHeap(), 0, LSData); 975 976 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_SAVEYOURSETTINGS); 977 978 UnloadUserProfile(Session->UserToken, Session->hProfileInfo); 979 980 CallNotificationDlls(Session, LogoffHandler); 981 982 CloseHandle(Session->UserToken); 983 UpdatePerUserSystemParameters(0, FALSE); 984 Session->LogonState = STATE_LOGGED_OFF; 985 Session->UserToken = NULL; 986 987 return STATUS_SUCCESS; 988 } 989 990 static 991 INT_PTR 992 CALLBACK 993 ShutdownComputerWindowProc( 994 IN HWND hwndDlg, 995 IN UINT uMsg, 996 IN WPARAM wParam, 997 IN LPARAM lParam) 998 { 999 UNREFERENCED_PARAMETER(lParam); 1000 1001 switch (uMsg) 1002 { 1003 case WM_COMMAND: 1004 { 1005 switch (LOWORD(wParam)) 1006 { 1007 case IDC_BTNSHTDOWNCOMPUTER: 1008 EndDialog(hwndDlg, IDC_BTNSHTDOWNCOMPUTER); 1009 return TRUE; 1010 } 1011 break; 1012 } 1013 case WM_INITDIALOG: 1014 { 1015 RemoveMenu(GetSystemMenu(hwndDlg, FALSE), SC_CLOSE, MF_BYCOMMAND); 1016 SetFocus(GetDlgItem(hwndDlg, IDC_BTNSHTDOWNCOMPUTER)); 1017 return TRUE; 1018 } 1019 } 1020 return FALSE; 1021 } 1022 1023 static 1024 VOID 1025 UninitializeSAS( 1026 IN OUT PWLSESSION Session) 1027 { 1028 if (Session->SASWindow) 1029 { 1030 DestroyWindow(Session->SASWindow); 1031 Session->SASWindow = NULL; 1032 } 1033 if (Session->hEndOfScreenSaverThread) 1034 SetEvent(Session->hEndOfScreenSaverThread); 1035 UnregisterClassW(WINLOGON_SAS_CLASS, hAppInstance); 1036 } 1037 1038 NTSTATUS 1039 HandleShutdown( 1040 IN OUT PWLSESSION Session, 1041 IN DWORD wlxAction) 1042 { 1043 PLOGOFF_SHUTDOWN_DATA LSData; 1044 HANDLE hThread; 1045 DWORD exitCode; 1046 BOOLEAN Old; 1047 1048 // SwitchDesktop(Session->WinlogonDesktop); 1049 DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_REACTOSISSHUTTINGDOWN); 1050 1051 /* Prepare data for shutdown thread */ 1052 LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA)); 1053 if (!LSData) 1054 { 1055 ERR("Failed to allocate mem for thread data\n"); 1056 return STATUS_NO_MEMORY; 1057 } 1058 if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) 1059 LSData->Flags = EWX_POWEROFF; 1060 else if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT) 1061 LSData->Flags = EWX_REBOOT; 1062 else 1063 LSData->Flags = EWX_SHUTDOWN; 1064 LSData->Session = Session; 1065 1066 // FIXME: We may need to specify this flag to really force application kill 1067 // (we are shutting down ReactOS, not just logging off so no hangs, etc... 1068 // should be allowed). 1069 // LSData->Flags |= EWX_FORCE; 1070 1071 /* Run shutdown thread */ 1072 hThread = CreateThread(NULL, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL); 1073 if (!hThread) 1074 { 1075 ERR("Unable to create shutdown thread, error %lu\n", GetLastError()); 1076 HeapFree(GetProcessHeap(), 0, LSData); 1077 return STATUS_UNSUCCESSFUL; 1078 } 1079 WaitForSingleObject(hThread, INFINITE); 1080 HeapFree(GetProcessHeap(), 0, LSData); 1081 if (!GetExitCodeThread(hThread, &exitCode)) 1082 { 1083 ERR("Unable to get exit code of shutdown thread (error %lu)\n", GetLastError()); 1084 CloseHandle(hThread); 1085 return STATUS_UNSUCCESSFUL; 1086 } 1087 CloseHandle(hThread); 1088 if (exitCode == 0) 1089 { 1090 ERR("Shutdown thread returned failure\n"); 1091 return STATUS_UNSUCCESSFUL; 1092 } 1093 1094 CallNotificationDlls(Session, ShutdownHandler); 1095 1096 /* Destroy SAS window */ 1097 UninitializeSAS(Session); 1098 1099 /* Now we can shut down NT */ 1100 ERR("Shutting down NT...\n"); 1101 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old); 1102 if (wlxAction == WLX_SAS_ACTION_SHUTDOWN_REBOOT) 1103 { 1104 NtShutdownSystem(ShutdownReboot); 1105 } 1106 else 1107 { 1108 if (FALSE) 1109 { 1110 /* FIXME - only show this dialog if it's a shutdown and the computer doesn't support APM */ 1111 DialogBox(hAppInstance, MAKEINTRESOURCE(IDD_SHUTDOWNCOMPUTER), 1112 GetDesktopWindow(), ShutdownComputerWindowProc); 1113 } 1114 NtShutdownSystem(ShutdownNoReboot); 1115 } 1116 RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, Old, FALSE, &Old); 1117 return STATUS_SUCCESS; 1118 } 1119 1120 static 1121 VOID 1122 DoGenericAction( 1123 IN OUT PWLSESSION Session, 1124 IN DWORD wlxAction) 1125 { 1126 switch (wlxAction) 1127 { 1128 case WLX_SAS_ACTION_LOGON: /* 0x01 */ 1129 if (Session->LogonState == STATE_LOGGED_OFF_SAS) 1130 { 1131 if (!HandleLogon(Session)) 1132 { 1133 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context); 1134 CallNotificationDlls(Session, LogonHandler); 1135 } 1136 } 1137 break; 1138 case WLX_SAS_ACTION_NONE: /* 0x02 */ 1139 if (Session->LogonState == STATE_LOGGED_OFF_SAS) 1140 { 1141 Session->LogonState = STATE_LOGGED_OFF; 1142 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context); 1143 } 1144 else if (Session->LogonState == STATE_LOGGED_ON_SAS) 1145 { 1146 Session->LogonState = STATE_LOGGED_ON; 1147 } 1148 else if (Session->LogonState == STATE_LOCKED_SAS) 1149 { 1150 Session->LogonState = STATE_LOCKED; 1151 Session->Gina.Functions.WlxDisplayLockedNotice(Session->Gina.Context); 1152 } 1153 break; 1154 case WLX_SAS_ACTION_LOCK_WKSTA: /* 0x03 */ 1155 if (Session->Gina.Functions.WlxIsLockOk(Session->Gina.Context)) 1156 { 1157 SwitchDesktop(Session->WinlogonDesktop); 1158 Session->LogonState = STATE_LOCKED; 1159 Session->Gina.Functions.WlxDisplayLockedNotice(Session->Gina.Context); 1160 CallNotificationDlls(Session, LockHandler); 1161 } 1162 break; 1163 case WLX_SAS_ACTION_LOGOFF: /* 0x04 */ 1164 case WLX_SAS_ACTION_SHUTDOWN: /* 0x05 */ 1165 case WLX_SAS_ACTION_SHUTDOWN_POWER_OFF: /* 0x0a */ 1166 case WLX_SAS_ACTION_SHUTDOWN_REBOOT: /* 0x0b */ 1167 if (Session->LogonState != STATE_LOGGED_OFF) 1168 { 1169 if (!Session->Gina.Functions.WlxIsLogoffOk(Session->Gina.Context)) 1170 break; 1171 if (!NT_SUCCESS(HandleLogoff(Session, EWX_LOGOFF))) 1172 { 1173 RemoveStatusMessage(Session); 1174 break; 1175 } 1176 Session->Gina.Functions.WlxLogoff(Session->Gina.Context); 1177 } 1178 if (WLX_SHUTTINGDOWN(wlxAction)) 1179 { 1180 // FIXME: WlxShutdown should be done from inside HandleShutdown, 1181 // after having displayed "ReactOS is shutting down" message. 1182 Session->Gina.Functions.WlxShutdown(Session->Gina.Context, wlxAction); 1183 if (!NT_SUCCESS(HandleShutdown(Session, wlxAction))) 1184 { 1185 RemoveStatusMessage(Session); 1186 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context); 1187 } 1188 } 1189 else 1190 { 1191 RemoveStatusMessage(Session); 1192 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context); 1193 } 1194 break; 1195 case WLX_SAS_ACTION_TASKLIST: /* 0x07 */ 1196 SwitchDesktop(Session->ApplicationDesktop); 1197 Session->LogonState = STATE_LOGGED_ON; 1198 StartTaskManager(Session); 1199 break; 1200 case WLX_SAS_ACTION_UNLOCK_WKSTA: /* 0x08 */ 1201 SwitchDesktop(Session->ApplicationDesktop); 1202 Session->LogonState = STATE_LOGGED_ON; 1203 CallNotificationDlls(Session, UnlockHandler); 1204 break; 1205 default: 1206 WARN("Unknown SAS action 0x%lx\n", wlxAction); 1207 } 1208 } 1209 1210 static 1211 VOID 1212 DispatchSAS( 1213 IN OUT PWLSESSION Session, 1214 IN DWORD dwSasType) 1215 { 1216 DWORD wlxAction = WLX_SAS_ACTION_NONE; 1217 PSID LogonSid = NULL; /* FIXME */ 1218 BOOL bSecure = TRUE; 1219 1220 switch (dwSasType) 1221 { 1222 case WLX_SAS_TYPE_CTRL_ALT_DEL: 1223 switch (Session->LogonState) 1224 { 1225 case STATE_INIT: 1226 Session->LogonState = STATE_LOGGED_OFF; 1227 Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context); 1228 return; 1229 1230 case STATE_LOGGED_OFF: 1231 Session->LogonState = STATE_LOGGED_OFF_SAS; 1232 1233 CloseAllDialogWindows(); 1234 1235 Session->Options = 0; 1236 1237 wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOutSAS( 1238 Session->Gina.Context, 1239 Session->SASAction, 1240 &Session->LogonId, 1241 LogonSid, 1242 &Session->Options, 1243 &Session->UserToken, 1244 &Session->MprNotifyInfo, 1245 (PVOID*)&Session->Profile); 1246 break; 1247 1248 case STATE_LOGGED_OFF_SAS: 1249 /* Ignore SAS if we are already in an SAS state */ 1250 return; 1251 1252 case STATE_LOGGED_ON: 1253 Session->LogonState = STATE_LOGGED_ON_SAS; 1254 wlxAction = (DWORD)Session->Gina.Functions.WlxLoggedOnSAS(Session->Gina.Context, dwSasType, NULL); 1255 break; 1256 1257 case STATE_LOGGED_ON_SAS: 1258 /* Ignore SAS if we are already in an SAS state */ 1259 return; 1260 1261 case STATE_LOCKED: 1262 Session->LogonState = STATE_LOCKED_SAS; 1263 1264 CloseAllDialogWindows(); 1265 1266 wlxAction = (DWORD)Session->Gina.Functions.WlxWkstaLockedSAS(Session->Gina.Context, dwSasType); 1267 break; 1268 1269 case STATE_LOCKED_SAS: 1270 /* Ignore SAS if we are already in an SAS state */ 1271 return; 1272 1273 default: 1274 return; 1275 } 1276 break; 1277 1278 case WLX_SAS_TYPE_TIMEOUT: 1279 return; 1280 1281 case WLX_SAS_TYPE_SCRNSVR_TIMEOUT: 1282 if (!Session->Gina.Functions.WlxScreenSaverNotify(Session->Gina.Context, &bSecure)) 1283 { 1284 /* Skip start of screen saver */ 1285 SetEvent(Session->hEndOfScreenSaver); 1286 } 1287 else 1288 { 1289 StartScreenSaver(Session); 1290 if (bSecure) 1291 { 1292 wlxAction = WLX_SAS_ACTION_LOCK_WKSTA; 1293 // DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA); 1294 } 1295 } 1296 break; 1297 1298 case WLX_SAS_TYPE_SCRNSVR_ACTIVITY: 1299 SetEvent(Session->hUserActivity); 1300 break; 1301 } 1302 1303 DoGenericAction(Session, wlxAction); 1304 } 1305 1306 static 1307 BOOL 1308 RegisterHotKeys( 1309 IN PWLSESSION Session, 1310 IN HWND hwndSAS) 1311 { 1312 /* Register Ctrl+Alt+Del Hotkey */ 1313 if (!RegisterHotKey(hwndSAS, HK_CTRL_ALT_DEL, MOD_CONTROL | MOD_ALT, VK_DELETE)) 1314 { 1315 ERR("WL: Unable to register Ctrl+Alt+Del hotkey!\n"); 1316 return FALSE; 1317 } 1318 1319 /* Register Ctrl+Shift+Esc (optional) */ 1320 Session->TaskManHotkey = RegisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC, MOD_CONTROL | MOD_SHIFT, VK_ESCAPE); 1321 if (!Session->TaskManHotkey) 1322 WARN("WL: Warning: Unable to register Ctrl+Alt+Esc hotkey!\n"); 1323 return TRUE; 1324 } 1325 1326 static 1327 BOOL 1328 UnregisterHotKeys( 1329 IN PWLSESSION Session, 1330 IN HWND hwndSAS) 1331 { 1332 /* Unregister hotkeys */ 1333 UnregisterHotKey(hwndSAS, HK_CTRL_ALT_DEL); 1334 1335 if (Session->TaskManHotkey) 1336 UnregisterHotKey(hwndSAS, HK_CTRL_SHIFT_ESC); 1337 1338 return TRUE; 1339 } 1340 1341 BOOL 1342 WINAPI 1343 HandleMessageBeep(UINT uType) 1344 { 1345 LPWSTR EventName; 1346 1347 switch(uType) 1348 { 1349 case 0xFFFFFFFF: 1350 EventName = NULL; 1351 break; 1352 case MB_OK: 1353 EventName = L"SystemDefault"; 1354 break; 1355 case MB_ICONASTERISK: 1356 EventName = L"SystemAsterisk"; 1357 break; 1358 case MB_ICONEXCLAMATION: 1359 EventName = L"SystemExclamation"; 1360 break; 1361 case MB_ICONHAND: 1362 EventName = L"SystemHand"; 1363 break; 1364 case MB_ICONQUESTION: 1365 EventName = L"SystemQuestion"; 1366 break; 1367 default: 1368 WARN("Unhandled type %d\n", uType); 1369 EventName = L"SystemDefault"; 1370 } 1371 1372 return PlaySoundRoutine(EventName, FALSE, SND_ALIAS | SND_NOWAIT | SND_NOSTOP | SND_ASYNC); 1373 } 1374 1375 static 1376 LRESULT 1377 CALLBACK 1378 SASWindowProc( 1379 IN HWND hwndDlg, 1380 IN UINT uMsg, 1381 IN WPARAM wParam, 1382 IN LPARAM lParam) 1383 { 1384 PWLSESSION Session = (PWLSESSION)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); 1385 1386 switch (uMsg) 1387 { 1388 case WM_HOTKEY: 1389 { 1390 switch (lParam) 1391 { 1392 case MAKELONG(MOD_CONTROL | MOD_ALT, VK_DELETE): 1393 { 1394 TRACE("SAS: CONTROL+ALT+DELETE\n"); 1395 if (!Session->Gina.UseCtrlAltDelete) 1396 break; 1397 PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_CTRL_ALT_DEL, 0); 1398 return TRUE; 1399 } 1400 case MAKELONG(MOD_CONTROL | MOD_SHIFT, VK_ESCAPE): 1401 { 1402 TRACE("SAS: CONTROL+SHIFT+ESCAPE\n"); 1403 if (Session->LogonState == STATE_LOGGED_ON) 1404 DoGenericAction(Session, WLX_SAS_ACTION_TASKLIST); 1405 return TRUE; 1406 } 1407 } 1408 break; 1409 } 1410 case WM_CREATE: 1411 { 1412 /* Get the session pointer from the create data */ 1413 Session = (PWLSESSION)((LPCREATESTRUCT)lParam)->lpCreateParams; 1414 1415 /* Save the Session pointer */ 1416 SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)Session); 1417 if (GetSetupType()) 1418 return TRUE; 1419 return RegisterHotKeys(Session, hwndDlg); 1420 } 1421 case WM_DESTROY: 1422 { 1423 if (!GetSetupType()) 1424 UnregisterHotKeys(Session, hwndDlg); 1425 return TRUE; 1426 } 1427 case WM_SETTINGCHANGE: 1428 { 1429 UINT uiAction = (UINT)wParam; 1430 if (uiAction == SPI_SETSCREENSAVETIMEOUT 1431 || uiAction == SPI_SETSCREENSAVEACTIVE) 1432 { 1433 SetEvent(Session->hScreenSaverParametersChanged); 1434 } 1435 return TRUE; 1436 } 1437 case WM_LOGONNOTIFY: 1438 { 1439 switch(wParam) 1440 { 1441 case LN_MESSAGE_BEEP: 1442 { 1443 return HandleMessageBeep(lParam); 1444 } 1445 case LN_SHELL_EXITED: 1446 { 1447 /* lParam is the exit code */ 1448 if (lParam != 1 && 1449 Session->LogonState != STATE_LOGGED_OFF && 1450 Session->LogonState != STATE_LOGGED_OFF_SAS) 1451 { 1452 SetTimer(hwndDlg, 1, 1000, NULL); 1453 } 1454 break; 1455 } 1456 case LN_START_SCREENSAVE: 1457 { 1458 DispatchSAS(Session, WLX_SAS_TYPE_SCRNSVR_TIMEOUT); 1459 break; 1460 } 1461 case LN_LOCK_WORKSTATION: 1462 { 1463 DoGenericAction(Session, WLX_SAS_ACTION_LOCK_WKSTA); 1464 break; 1465 } 1466 case LN_LOGOFF: 1467 { 1468 UINT Flags = (UINT)lParam; 1469 UINT Action = Flags & EWX_ACTION_MASK; 1470 DWORD wlxAction; 1471 1472 TRACE("\tFlags : 0x%lx\n", lParam); 1473 1474 /* 1475 * Our caller (USERSRV) should have added the shutdown flag 1476 * when setting also poweroff or reboot. 1477 */ 1478 if (Action & (EWX_POWEROFF | EWX_REBOOT)) 1479 { 1480 if ((Action & EWX_SHUTDOWN) == 0) 1481 { 1482 ERR("Missing EWX_SHUTDOWN flag for poweroff or reboot; action 0x%x\n", Action); 1483 return STATUS_INVALID_PARAMETER; 1484 } 1485 1486 /* Now we can locally remove it for performing checks */ 1487 Action &= ~EWX_SHUTDOWN; 1488 } 1489 1490 /* Check parameters */ 1491 if (Action & EWX_FORCE) 1492 { 1493 // FIXME! 1494 ERR("FIXME: EWX_FORCE present for Winlogon, what to do?\n"); 1495 Action &= ~EWX_FORCE; 1496 } 1497 switch (Action) 1498 { 1499 case EWX_LOGOFF: 1500 wlxAction = WLX_SAS_ACTION_LOGOFF; 1501 break; 1502 case EWX_SHUTDOWN: 1503 wlxAction = WLX_SAS_ACTION_SHUTDOWN; 1504 break; 1505 case EWX_REBOOT: 1506 wlxAction = WLX_SAS_ACTION_SHUTDOWN_REBOOT; 1507 break; 1508 case EWX_POWEROFF: 1509 wlxAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; 1510 break; 1511 1512 default: 1513 { 1514 ERR("Invalid ExitWindows action 0x%x\n", Action); 1515 return STATUS_INVALID_PARAMETER; 1516 } 1517 } 1518 1519 TRACE("In LN_LOGOFF, exit_in_progress == %s\n", 1520 ExitReactOSInProgress ? "true" : "false"); 1521 1522 /* 1523 * In case a parallel shutdown request is done (while we are 1524 * being to shut down) and it was not done by Winlogon itself, 1525 * then just stop here. 1526 */ 1527 #if 0 1528 // This code is commented at the moment (even if it's correct) because 1529 // our log-offs do not really work: the shell is restarted, no app is killed 1530 // etc... and as a result you just get explorer opening "My Documents". And 1531 // if you try now a shut down, it won't work because winlogon thinks it is 1532 // still in the middle of a shutdown. 1533 // Maybe we also need to reset ExitReactOSInProgress somewhere else?? 1534 if (ExitReactOSInProgress && (lParam & EWX_CALLER_WINLOGON) == 0) 1535 { 1536 break; 1537 } 1538 #endif 1539 /* Now do the shutdown action proper */ 1540 DoGenericAction(Session, wlxAction); 1541 return 1; 1542 } 1543 case LN_LOGOFF_CANCELED: 1544 { 1545 ERR("Logoff canceled!!, before: exit_in_progress == %s, after will be false\n", 1546 ExitReactOSInProgress ? "true" : "false"); 1547 1548 ExitReactOSInProgress = FALSE; 1549 return 1; 1550 } 1551 default: 1552 { 1553 ERR("WM_LOGONNOTIFY case %d is unimplemented\n", wParam); 1554 } 1555 } 1556 return 0; 1557 } 1558 case WM_TIMER: 1559 { 1560 if (wParam == 1) 1561 { 1562 KillTimer(hwndDlg, 1); 1563 StartUserShell(Session); 1564 } 1565 break; 1566 } 1567 case WLX_WM_SAS: 1568 { 1569 DispatchSAS(Session, (DWORD)wParam); 1570 return TRUE; 1571 } 1572 } 1573 1574 return DefWindowProc(hwndDlg, uMsg, wParam, lParam); 1575 } 1576 1577 BOOL 1578 InitializeSAS( 1579 IN OUT PWLSESSION Session) 1580 { 1581 WNDCLASSEXW swc; 1582 BOOL ret = FALSE; 1583 1584 if (!SwitchDesktop(Session->WinlogonDesktop)) 1585 { 1586 ERR("WL: Failed to switch to winlogon desktop\n"); 1587 goto cleanup; 1588 } 1589 1590 /* Register SAS window class */ 1591 swc.cbSize = sizeof(WNDCLASSEXW); 1592 swc.style = CS_SAVEBITS; 1593 swc.lpfnWndProc = SASWindowProc; 1594 swc.cbClsExtra = 0; 1595 swc.cbWndExtra = 0; 1596 swc.hInstance = hAppInstance; 1597 swc.hIcon = NULL; 1598 swc.hCursor = NULL; 1599 swc.hbrBackground = NULL; 1600 swc.lpszMenuName = NULL; 1601 swc.lpszClassName = WINLOGON_SAS_CLASS; 1602 swc.hIconSm = NULL; 1603 if (RegisterClassExW(&swc) == 0) 1604 { 1605 ERR("WL: Failed to register SAS window class\n"); 1606 goto cleanup; 1607 } 1608 1609 /* Create invisible SAS window */ 1610 Session->SASWindow = CreateWindowExW( 1611 0, 1612 WINLOGON_SAS_CLASS, 1613 WINLOGON_SAS_TITLE, 1614 WS_POPUP, 1615 0, 0, 0, 0, 0, 0, 1616 hAppInstance, Session); 1617 if (!Session->SASWindow) 1618 { 1619 ERR("WL: Failed to create SAS window\n"); 1620 goto cleanup; 1621 } 1622 1623 /* Register SAS window to receive SAS notifications */ 1624 if (!SetLogonNotifyWindow(Session->SASWindow)) 1625 { 1626 ERR("WL: Failed to register SAS window\n"); 1627 goto cleanup; 1628 } 1629 1630 if (!SetDefaultLanguage(NULL)) 1631 return FALSE; 1632 1633 ret = TRUE; 1634 1635 cleanup: 1636 if (!ret) 1637 UninitializeSAS(Session); 1638 return ret; 1639 } 1640