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