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