Reactos
at master 1126 lines 31 kB view raw
1/* 2 * ReactOS GINA 3 * Copyright (C) 2003-2004, 2006 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19/* 20 * PROJECT: ReactOS msgina.dll 21 * FILE: dll/win32/msgina/msgina.c 22 * PURPOSE: ReactOS Logon GINA DLL 23 * PROGRAMMER: Thomas Weidenmueller (w3seek@users.sourceforge.net) 24 * Hervé Poussineau (hpoussin@reactos.org) 25 */ 26 27#include "msgina.h" 28 29#include <winsvc.h> 30#include <userenv.h> 31#include <ndk/sefuncs.h> 32 33HINSTANCE hDllInstance; 34 35extern GINA_UI GinaGraphicalUI; 36extern GINA_UI GinaTextUI; 37static PGINA_UI pGinaUI; 38static SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY}; 39static PSID AdminSid; 40 41/* 42 * @implemented 43 */ 44BOOL WINAPI 45WlxNegotiate( 46 IN DWORD dwWinlogonVersion, 47 OUT PDWORD pdwDllVersion) 48{ 49 TRACE("WlxNegotiate(%lx, %p)\n", dwWinlogonVersion, pdwDllVersion); 50 51 if(!pdwDllVersion || (dwWinlogonVersion < WLX_VERSION_1_3)) 52 return FALSE; 53 54 *pdwDllVersion = WLX_VERSION_1_3; 55 56 return TRUE; 57} 58 59static VOID 60ChooseGinaUI(VOID) 61{ 62 HKEY ControlKey = NULL; 63 LPWSTR SystemStartOptions = NULL; 64 LPWSTR CurrentOption, NextOption; /* Pointers into SystemStartOptions */ 65 BOOL ConsoleBoot = FALSE; 66 LONG rc; 67 68 rc = RegOpenKeyExW( 69 HKEY_LOCAL_MACHINE, 70 L"SYSTEM\\CurrentControlSet\\Control", 71 0, 72 KEY_QUERY_VALUE, 73 &ControlKey); 74 75 rc = ReadRegSzValue(ControlKey, L"SystemStartOptions", &SystemStartOptions); 76 if (rc != ERROR_SUCCESS) 77 goto cleanup; 78 79 /* Check for CONSOLE switch in SystemStartOptions */ 80 CurrentOption = SystemStartOptions; 81 while (CurrentOption) 82 { 83 NextOption = wcschr(CurrentOption, L' '); 84 if (NextOption) 85 *NextOption = L'\0'; 86 if (_wcsicmp(CurrentOption, L"CONSOLE") == 0) 87 { 88 TRACE("Found %S. Switching to console boot\n", CurrentOption); 89 ConsoleBoot = TRUE; 90 goto cleanup; 91 } 92 CurrentOption = NextOption ? NextOption + 1 : NULL; 93 } 94 95cleanup: 96 if (ConsoleBoot) 97 pGinaUI = &GinaTextUI; 98 else 99 pGinaUI = &GinaGraphicalUI; 100 101 if (ControlKey != NULL) 102 RegCloseKey(ControlKey); 103 HeapFree(GetProcessHeap(), 0, SystemStartOptions); 104} 105 106static BOOL 107SafeGetUnicodeString( 108 _In_ const LSA_UNICODE_STRING *pInput, 109 _Out_ PWSTR pszOutput, 110 _In_ SIZE_T cchMax) 111{ 112 HRESULT hr; 113 hr = StringCbCopyNExW(pszOutput, cchMax * sizeof(WCHAR), 114 pInput->Buffer, pInput->Length, 115 NULL, NULL, 116 STRSAFE_NO_TRUNCATION | STRSAFE_NULL_ON_FAILURE); 117 return (hr == S_OK); 118} 119 120/* Reference: https://learn.microsoft.com/en-us/windows/win32/secauthn/protecting-the-automatic-logon-password */ 121static BOOL 122GetLsaDefaultPassword(_Inout_ PGINA_CONTEXT pgContext) 123{ 124 LSA_HANDLE hPolicy; 125 LSA_UNICODE_STRING Name, *pPwd; 126 LSA_OBJECT_ATTRIBUTES ObjectAttributes = { sizeof(ObjectAttributes) }; 127 128 NTSTATUS Status = LsaOpenPolicy(NULL, &ObjectAttributes, 129 POLICY_GET_PRIVATE_INFORMATION, &hPolicy); 130 if (!NT_SUCCESS(Status)) 131 return FALSE; 132 133 RtlInitUnicodeString(&Name, L"DefaultPassword"); 134 Status = LsaRetrievePrivateData(hPolicy, &Name, &pPwd); 135 LsaClose(hPolicy); 136 137 if (Status == STATUS_SUCCESS) 138 { 139 if (!SafeGetUnicodeString(pPwd, pgContext->Password, 140 _countof(pgContext->Password))) 141 { 142 Status = STATUS_BUFFER_TOO_SMALL; 143 } 144 SecureZeroMemory(pPwd->Buffer, pPwd->Length); 145 LsaFreeMemory(pPwd); 146 } 147 148 return Status == STATUS_SUCCESS; 149} 150 151static 152BOOL 153GetRegistrySettings(PGINA_CONTEXT pgContext) 154{ 155 HKEY hKey = NULL; 156 DWORD dwValue, dwSize; 157 DWORD dwDisableCAD = 0; 158 LONG rc; 159 160 rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 161 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", 162 0, 163 KEY_QUERY_VALUE, 164 &hKey); 165 if (rc != ERROR_SUCCESS) 166 { 167 WARN("RegOpenKeyExW() failed with error %lu\n", rc); 168 return FALSE; 169 } 170 171 rc = ReadRegDwordValue(hKey, L"AutoAdminLogon", &dwValue); 172 if (rc == ERROR_SUCCESS) 173 pgContext->bAutoAdminLogon = !!dwValue; 174 TRACE("bAutoAdminLogon: %s\n", pgContext->bAutoAdminLogon ? "TRUE" : "FALSE"); 175 176 // TODO: What to do also depends whether we are on Terminal Services. 177 rc = ReadRegDwordValue(hKey, L"DisableCAD", &dwDisableCAD); 178 if (rc == ERROR_SUCCESS) 179 { 180 if (dwDisableCAD != 0) 181 pgContext->bDisableCAD = TRUE; 182 } 183 TRACE("bDisableCAD: %s\n", pgContext->bDisableCAD ? "TRUE" : "FALSE"); 184 185 // NOTE: The default value is always read from the registry (Workstation: TRUE; Server: FALSE). 186 // TODO: Set it to TRUE always on SafeMode. Keep it FALSE for remote sessions. 187 pgContext->bShutdownWithoutLogon = TRUE; 188 rc = ReadRegDwordValue(hKey, L"ShutdownWithoutLogon", &dwValue); 189 if (rc == ERROR_SUCCESS) 190 pgContext->bShutdownWithoutLogon = !!dwValue; 191 192 rc = ReadRegDwordValue(hKey, L"DontDisplayLastUserName", &dwValue); 193 if (rc == ERROR_SUCCESS) 194 pgContext->bDontDisplayLastUserName = !!dwValue; 195 196 rc = ReadRegDwordValue(hKey, L"IgnoreShiftOverride", &dwValue); 197 if (rc == ERROR_SUCCESS) 198 pgContext->bIgnoreShiftOverride = !!dwValue; 199 200 dwSize = sizeof(pgContext->UserName); 201 rc = RegQueryValueExW(hKey, 202 L"DefaultUserName", 203 NULL, 204 NULL, 205 (PBYTE)&pgContext->UserName, 206 &dwSize); 207 208 dwSize = sizeof(pgContext->DomainName); 209 rc = RegQueryValueExW(hKey, 210 L"DefaultDomainName", 211 NULL, 212 NULL, 213 (PBYTE)&pgContext->DomainName, 214 &dwSize); 215 216 dwSize = sizeof(pgContext->Password); 217 rc = RegQueryValueExW(hKey, 218 L"DefaultPassword", 219 NULL, 220 NULL, 221 (PBYTE)&pgContext->Password, 222 &dwSize); 223 if (rc) 224 GetLsaDefaultPassword(pgContext); 225 226 if (hKey != NULL) 227 RegCloseKey(hKey); 228 229 return TRUE; 230} 231 232typedef DWORD (WINAPI *pThemeWait)(DWORD dwTimeout); 233typedef BOOL (WINAPI *pThemeWatch)(void); 234 235static void 236InitThemeSupport(VOID) 237{ 238 HMODULE hDll = LoadLibraryW(L"shsvcs.dll"); 239 pThemeWait themeWait; 240 pThemeWatch themeWatch; 241 242 if(!hDll) 243 return; 244 245 themeWait = (pThemeWait) GetProcAddress(hDll, (LPCSTR)2); 246 themeWatch = (pThemeWatch) GetProcAddress(hDll, (LPCSTR)1); 247 248 if(themeWait && themeWatch) 249 { 250 themeWait(5000); 251 themeWatch(); 252 } 253} 254 255/* 256 * @implemented 257 */ 258BOOL WINAPI 259WlxInitialize( 260 LPWSTR lpWinsta, 261 HANDLE hWlx, 262 PVOID pvReserved, 263 PVOID pWinlogonFunctions, 264 PVOID *pWlxContext) 265{ 266 PGINA_CONTEXT pgContext; 267 268 UNREFERENCED_PARAMETER(pvReserved); 269 270 InitThemeSupport(); 271 272 pgContext = (PGINA_CONTEXT)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(GINA_CONTEXT)); 273 if(!pgContext) 274 { 275 WARN("LocalAlloc() failed\n"); 276 return FALSE; 277 } 278 279 if (!GetRegistrySettings(pgContext)) 280 { 281 WARN("GetRegistrySettings() failed\n"); 282 LocalFree(pgContext); 283 return FALSE; 284 } 285 286 /* Return the context to winlogon */ 287 *pWlxContext = (PVOID)pgContext; 288 pgContext->hDllInstance = hDllInstance; 289 290 /* Save pointer to dispatch table */ 291 pgContext->pWlxFuncs = (PWLX_DISPATCH_VERSION_1_3)pWinlogonFunctions; 292 293 /* Save the winlogon handle used to call the dispatch functions */ 294 pgContext->hWlx = hWlx; 295 296 /* Save window station */ 297 pgContext->station = lpWinsta; 298 299 /* Clear status window handle */ 300 pgContext->hStatusWindow = NULL; 301 302 /* Notify winlogon that we will use the default SAS */ 303 pgContext->pWlxFuncs->WlxUseCtrlAltDel(hWlx); 304 305 /* Locates the authentication package */ 306 //LsaRegisterLogonProcess(...); 307 308 pgContext->nShutdownAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF; 309 310 ChooseGinaUI(); 311 return pGinaUI->Initialize(pgContext); 312} 313 314/* 315 * @implemented 316 */ 317BOOL 318WINAPI 319WlxScreenSaverNotify( 320 PVOID pWlxContext, 321 BOOL *pSecure) 322{ 323#if 0 324 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 325 WCHAR szBuffer[2]; 326 HKEY hKeyCurrentUser, hKey; 327 DWORD bufferSize = sizeof(szBuffer); 328 DWORD varType = REG_SZ; 329 LONG rc; 330 331 TRACE("(%p %p)\n", pWlxContext, pSecure); 332 333 *pSecure = TRUE; 334 335 /* 336 * Policy setting: 337 * HKLM\Software\Policies\Microsoft\Windows\Control Panel\Desktop : ScreenSaverIsSecure 338 * User setting: 339 * HKCU\Control Panel\Desktop : ScreenSaverIsSecure 340 */ 341 342 if (!ImpersonateLoggedOnUser(pgContext->UserToken)) 343 { 344 ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError()); 345 *pSecure = FALSE; 346 return TRUE; 347 } 348 349 /* Open the current user HKCU key */ 350 rc = RegOpenCurrentUser(MAXIMUM_ALLOWED, &hKeyCurrentUser); 351 TRACE("RegOpenCurrentUser: %ld\n", rc); 352 if (rc == ERROR_SUCCESS) 353 { 354 /* Open the subkey */ 355 rc = RegOpenKeyExW(hKeyCurrentUser, 356 L"Control Panel\\Desktop", 357 0, 358 KEY_QUERY_VALUE, 359 &hKey); 360 TRACE("RegOpenKeyExW: %ld\n", rc); 361 RegCloseKey(hKeyCurrentUser); 362 } 363 364 /* Read the value */ 365 if (rc == ERROR_SUCCESS) 366 { 367 rc = RegQueryValueExW(hKey, 368 L"ScreenSaverIsSecure", 369 NULL, 370 &varType, 371 (LPBYTE)szBuffer, 372 &bufferSize); 373 374 TRACE("RegQueryValueExW: %ld\n", rc); 375 376 if (rc == ERROR_SUCCESS) 377 { 378 TRACE("szBuffer: \"%S\"\n", szBuffer); 379 *pSecure = _wtoi(szBuffer); 380 } 381 382 RegCloseKey(hKey); 383 } 384 385 /* Revert the impersonation */ 386 RevertToSelf(); 387 388 TRACE("*pSecure: %ld\n", *pSecure); 389#endif 390 391 *pSecure = FALSE; 392 393 return TRUE; 394} 395 396/* 397 * @implemented 398 */ 399BOOL WINAPI 400WlxStartApplication( 401 PVOID pWlxContext, 402 PWSTR pszDesktopName, 403 PVOID pEnvironment, 404 PWSTR pszCmdLine) 405{ 406 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 407 STARTUPINFOW StartupInfo; 408 PROCESS_INFORMATION ProcessInformation; 409 WCHAR CurrentDirectory[MAX_PATH]; 410 HANDLE hAppToken; 411 UINT len; 412 BOOL ret; 413 414 len = GetWindowsDirectoryW(CurrentDirectory, MAX_PATH); 415 if (len == 0 || len > MAX_PATH) 416 { 417 ERR("GetWindowsDirectoryW() failed\n"); 418 return FALSE; 419 } 420 421 ret = DuplicateTokenEx(pgContext->UserToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &hAppToken); 422 if (!ret) 423 { 424 ERR("DuplicateTokenEx() failed with error %lu\n", GetLastError()); 425 return FALSE; 426 } 427 428 ZeroMemory(&StartupInfo, sizeof(StartupInfo)); 429 ZeroMemory(&ProcessInformation, sizeof(ProcessInformation)); 430 StartupInfo.cb = sizeof(StartupInfo); 431 StartupInfo.lpTitle = pszCmdLine; 432 StartupInfo.dwFlags = STARTF_USESHOWWINDOW; 433 StartupInfo.wShowWindow = SW_SHOW; 434 StartupInfo.lpDesktop = pszDesktopName; 435 436 len = GetWindowsDirectoryW(CurrentDirectory, MAX_PATH); 437 if (len == 0 || len > MAX_PATH) 438 { 439 ERR("GetWindowsDirectoryW() failed\n"); 440 return FALSE; 441 } 442 ret = CreateProcessAsUserW( 443 hAppToken, 444 pszCmdLine, 445 NULL, 446 NULL, 447 NULL, 448 FALSE, 449 CREATE_UNICODE_ENVIRONMENT, 450 pEnvironment, 451 CurrentDirectory, 452 &StartupInfo, 453 &ProcessInformation); 454 CloseHandle(ProcessInformation.hProcess); 455 CloseHandle(ProcessInformation.hThread); 456 CloseHandle(hAppToken); 457 if (!ret) 458 ERR("CreateProcessAsUserW() failed with error %lu\n", GetLastError()); 459 return ret; 460} 461 462/* 463 * @implemented 464 */ 465BOOL 466WINAPI 467WlxActivateUserShell( 468 _In_ PVOID pWlxContext, 469 _In_ PWSTR pszDesktopName, 470 _In_ PWSTR pszMprLogonScript, 471 _In_ PVOID pEnvironment) 472{ 473 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 474 HKEY hKey, hKeyCurrentUser; 475 DWORD BufSize, ValueType; 476 DWORD len; 477 LONG rc; 478 BOOL ret; 479 WCHAR pszUserInitApp[MAX_PATH + 1]; 480 WCHAR pszExpUserInitApp[MAX_PATH]; 481 482 TRACE("WlxActivateUserShell()\n"); 483 484 UNREFERENCED_PARAMETER(pszMprLogonScript); 485 486 /* Get the path of Userinit */ 487 rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 488 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", 489 0, 490 KEY_QUERY_VALUE, 491 &hKey); 492 if (rc != ERROR_SUCCESS) 493 { 494 WARN("RegOpenKeyExW() failed with error %lu\n", rc); 495 return FALSE; 496 } 497 498 BufSize = sizeof(pszUserInitApp) - sizeof(UNICODE_NULL); 499 rc = RegQueryValueExW(hKey, 500 L"Userinit", 501 NULL, 502 &ValueType, 503 (PBYTE)pszUserInitApp, 504 &BufSize); 505 RegCloseKey(hKey); 506 if (rc != ERROR_SUCCESS || (ValueType != REG_SZ && ValueType != REG_EXPAND_SZ)) 507 { 508 WARN("RegQueryValueExW() failed with error %lu\n", rc); 509 return FALSE; 510 } 511 pszUserInitApp[MAX_PATH] = UNICODE_NULL; 512 513 len = ExpandEnvironmentStringsW(pszUserInitApp, pszExpUserInitApp, _countof(pszExpUserInitApp)); 514 if (len > _countof(pszExpUserInitApp)) 515 { 516 WARN("ExpandEnvironmentStringsW() failed. Required size %lu\n", len); 517 return FALSE; 518 } 519 520 /* Start the Userinit application */ 521 ret = WlxStartApplication(pWlxContext, pszDesktopName, pEnvironment, pszExpUserInitApp); 522 if (!ret) 523 return ret; 524 525 /* For convenience, store in the logged-in user's Explorer key, the user name 526 * that was entered verbatim in the "Log On" dialog to log into the system. 527 * This name may differ from the resulting user name used during authentication. */ 528 529 /* Open the per-user registry key */ 530 rc = RegOpenLoggedOnHKCU(pgContext->UserToken, 531 KEY_SET_VALUE, 532 &hKeyCurrentUser); 533 if (rc != ERROR_SUCCESS) 534 { 535 ERR("RegOpenLoggedOnHKCU() failed with error %ld\n", rc); 536 return ret; 537 } 538 539 /* Open the subkey and write the value */ 540 rc = RegOpenKeyExW(hKeyCurrentUser, 541 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", 542 0, 543 KEY_SET_VALUE, 544 &hKey); 545 if (rc == ERROR_SUCCESS) 546 { 547 len = wcslen(pgContext->UserName) + 1; 548 RegSetValueExW(hKey, 549 L"Logon User Name", 550 0, 551 REG_SZ, 552 (PBYTE)pgContext->UserName, 553 len * sizeof(WCHAR)); 554 RegCloseKey(hKey); 555 } 556 RegCloseKey(hKeyCurrentUser); 557 558 return ret; 559} 560 561/* 562 * @implemented 563 */ 564int WINAPI 565WlxLoggedOnSAS( 566 PVOID pWlxContext, 567 DWORD dwSasType, 568 PVOID pReserved) 569{ 570 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 571 INT SasAction = WLX_SAS_ACTION_NONE; 572 573 TRACE("WlxLoggedOnSAS(0x%lx)\n", dwSasType); 574 575 UNREFERENCED_PARAMETER(pReserved); 576 577 switch (dwSasType) 578 { 579 case WLX_SAS_TYPE_CTRL_ALT_DEL: 580 case WLX_SAS_TYPE_TIMEOUT: 581 { 582 SasAction = pGinaUI->LoggedOnSAS(pgContext, dwSasType); 583 break; 584 } 585 case WLX_SAS_TYPE_SC_INSERT: 586 { 587 FIXME("WlxLoggedOnSAS: SasType WLX_SAS_TYPE_SC_INSERT not supported!\n"); 588 break; 589 } 590 case WLX_SAS_TYPE_SC_REMOVE: 591 { 592 FIXME("WlxLoggedOnSAS: SasType WLX_SAS_TYPE_SC_REMOVE not supported!\n"); 593 break; 594 } 595 default: 596 { 597 WARN("WlxLoggedOnSAS: Unknown SasType: 0x%x\n", dwSasType); 598 break; 599 } 600 } 601 602 return SasAction; 603} 604 605/* 606 * @implemented 607 */ 608BOOL WINAPI 609WlxDisplayStatusMessage( 610 IN PVOID pWlxContext, 611 IN HDESK hDesktop, 612 IN DWORD dwOptions, 613 IN PWSTR pTitle, 614 IN PWSTR pMessage) 615{ 616 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 617 618 TRACE("WlxDisplayStatusMessage(\"%S\")\n", pMessage); 619 620 return pGinaUI->DisplayStatusMessage(pgContext, hDesktop, dwOptions, pTitle, pMessage); 621} 622 623/* 624 * @implemented 625 */ 626BOOL WINAPI 627WlxRemoveStatusMessage( 628 IN PVOID pWlxContext) 629{ 630 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 631 632 TRACE("WlxRemoveStatusMessage()\n"); 633 634 return pGinaUI->RemoveStatusMessage(pgContext); 635} 636 637 638BOOL 639DoAdminUnlock( 640 IN PGINA_CONTEXT pgContext, 641 IN PWSTR UserName, 642 IN PWSTR Domain, 643 IN PWSTR Password) 644{ 645 HANDLE hToken = NULL; 646 PTOKEN_GROUPS Groups = NULL; 647 BOOL bIsAdmin = FALSE; 648 ULONG Size; 649 ULONG i; 650 NTSTATUS Status; 651 NTSTATUS SubStatus = STATUS_SUCCESS; 652 653 TRACE("(%S %S %S)\n", UserName, Domain, Password); 654 655 Status = ConnectToLsa(pgContext); 656 if (!NT_SUCCESS(Status)) 657 { 658 WARN("ConnectToLsa() failed\n"); 659 return FALSE; 660 } 661 662 Status = MyLogonUser(pgContext->LsaHandle, 663 pgContext->AuthenticationPackage, 664 UserName, 665 Domain, 666 Password, 667 &pgContext->UserToken, 668 &SubStatus); 669 if (!NT_SUCCESS(Status)) 670 { 671 WARN("MyLogonUser() failed\n"); 672 return FALSE; 673 } 674 675 Status = NtQueryInformationToken(hToken, 676 TokenGroups, 677 NULL, 678 0, 679 &Size); 680 if ((Status != STATUS_SUCCESS) && (Status != STATUS_BUFFER_TOO_SMALL)) 681 { 682 TRACE("NtQueryInformationToken() failed (Status 0x%08lx)\n", Status); 683 goto done; 684 } 685 686 Groups = HeapAlloc(GetProcessHeap(), 0, Size); 687 if (Groups == NULL) 688 { 689 TRACE("HeapAlloc() failed\n"); 690 goto done; 691 } 692 693 Status = NtQueryInformationToken(hToken, 694 TokenGroups, 695 Groups, 696 Size, 697 &Size); 698 if (!NT_SUCCESS(Status)) 699 { 700 TRACE("NtQueryInformationToken() failed (Status 0x%08lx)\n", Status); 701 goto done; 702 } 703 704 for (i = 0; i < Groups->GroupCount; i++) 705 { 706 if (RtlEqualSid(Groups->Groups[i].Sid, AdminSid)) 707 { 708 TRACE("Member of Admins group\n"); 709 bIsAdmin = TRUE; 710 break; 711 } 712 } 713 714done: 715 if (Groups != NULL) 716 HeapFree(GetProcessHeap(), 0, Groups); 717 718 if (hToken != NULL) 719 CloseHandle(hToken); 720 721 return bIsAdmin; 722} 723 724 725NTSTATUS 726DoLoginTasks( 727 IN OUT PGINA_CONTEXT pgContext, 728 IN PWSTR UserName, 729 IN PWSTR Domain, 730 IN PWSTR Password, 731 OUT PNTSTATUS SubStatus) 732{ 733 NTSTATUS Status; 734 735 Status = ConnectToLsa(pgContext); 736 if (!NT_SUCCESS(Status)) 737 { 738 WARN("ConnectToLsa() failed (Status 0x%08lx)\n", Status); 739 return Status; 740 } 741 742 Status = MyLogonUser(pgContext->LsaHandle, 743 pgContext->AuthenticationPackage, 744 UserName, 745 Domain, 746 Password, 747 &pgContext->UserToken, 748 SubStatus); 749 if (!NT_SUCCESS(Status)) 750 { 751 WARN("MyLogonUser() failed (Status 0x%08lx)\n", Status); 752 } 753 754 return Status; 755} 756 757 758BOOL 759CreateProfile( 760 IN OUT PGINA_CONTEXT pgContext, 761 IN PWSTR UserName, 762 IN PWSTR Domain, 763 IN PWSTR Password) 764{ 765 PWLX_PROFILE_V2_0 pProfile = NULL; 766 PWSTR pEnvironment = NULL; 767 TOKEN_STATISTICS Stats; 768 DWORD cbStats, cbSize; 769 DWORD dwLength; 770#if 0 771 BOOL bIsDomainLogon; 772 WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH+1]; 773#endif 774 775 /* Store the logon time in the context */ 776 GetLocalTime(&pgContext->LogonTime); 777 778 /* Store user and domain in the context */ 779 wcscpy(pgContext->UserName, UserName); 780 if (Domain == NULL || !Domain[0]) 781 { 782 dwLength = _countof(pgContext->DomainName); 783 GetComputerNameW(pgContext->DomainName, &dwLength); 784 } 785 else 786 { 787 wcscpy(pgContext->DomainName, Domain); 788 } 789 /* From now on we use in UserName and Domain the captured values from pgContext */ 790 UserName = pgContext->UserName; 791 Domain = pgContext->DomainName; 792 793#if 0 794 /* Determine whether this is really a domain logon, by verifying 795 * that the specified domain is different from the local computer */ 796 dwLength = _countof(ComputerName); 797 GetComputerNameW(ComputerName, &dwLength); 798 bIsDomainLogon = (_wcsicmp(ComputerName, Domain) != 0); 799#endif 800 801 /* Allocate memory for profile */ 802 pProfile = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(*pProfile)); 803 if (!pProfile) 804 { 805 WARN("HeapAlloc() failed\n"); 806 goto cleanup; 807 } 808 pProfile->dwType = WLX_PROFILE_TYPE_V2_0; 809 810 /* 811 * TODO: For domain logon support: 812 * 813 * - pszProfile: Specifies the path to a *roaming* user profile on a 814 * domain server, if any. It is then used to create a local image 815 * (copy) of the profile on the local computer. 816 * ** This data should be retrieved from the LsaLogonUser() call 817 * made by MyLogonUser()! ** 818 * 819 * - pszPolicy (for domain logon): Path to a policy file. 820 * Windows' msgina.dll hardcodes it as: 821 * "\\<domain_controller>\netlogon\ntconfig.pol" 822 * 823 * - pszNetworkDefaultUserProfile (for domain logon): Path to the 824 * default user profile. Windows' msgina.dll hardcodes it as: 825 * "\\<domain_controller>\netlogon\Default User" 826 * 827 * - pszServerName (for domain logon): Name ("domain_controller") of 828 * the server (local computer; Active Directory domain controller...) 829 * that validated the logon. 830 * ** This data should be retrieved from the LsaLogonUser() call 831 * made by MyLogonUser()! ** 832 * 833 * NOTES: 834 * - The paths use the domain controllers' "netlogon" share. 835 * - These strings are LocalAlloc'd here, and LocalFree'd by Winlogon. 836 */ 837 pProfile->pszProfile = NULL; 838 pProfile->pszPolicy = NULL; 839 pProfile->pszNetworkDefaultUserProfile = NULL; 840 pProfile->pszServerName = NULL; 841#if 0 842 if (bIsDomainLogon) 843 { 844 PWSTR pServerName; 845 cbSize = sizeof(L"\\\\") + wcslen(Domain) * sizeof(WCHAR); 846 pServerName = LocalAlloc(LMEM_FIXED, cbSize); 847 if (!pServerName) 848 WARN("HeapAlloc() failed\n"); // Consider this optional, so no failure. 849 else 850 StringCbPrintfW(pServerName, cbSize, L"\\\\%ws", Domain); // See LogonServer below. 851 pProfile->pszServerName = pServerName; 852 } 853#endif 854 855 /* Build the minimal environment string block */ 856 // FIXME: LogonServer is the name of the server that processed the logon 857 // request ("domain_controller"). It can be different from the selected 858 // user's logon domain. 859 // See e.g.: 860 // - https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-msv1_0_interactive_profile 861 // - https://learn.microsoft.com/en-us/windows/win32/api/winwlx/ns-winwlx-wlx_consoleswitch_credentials_info_v1_0 862 cbSize = sizeof(L"LOGONSERVER=\\\\") + 863 wcslen(Domain) * sizeof(WCHAR) + 864 sizeof(UNICODE_NULL); 865 pEnvironment = LocalAlloc(LMEM_FIXED, cbSize); 866 if (!pEnvironment) 867 { 868 WARN("LocalAlloc() failed\n"); 869 goto cleanup; 870 } 871 872 StringCbPrintfW(pEnvironment, cbSize, L"LOGONSERVER=\\\\%ws", Domain); 873 ASSERT(wcslen(pEnvironment) == cbSize / sizeof(WCHAR) - 2); 874 pEnvironment[cbSize / sizeof(WCHAR) - 1] = UNICODE_NULL; 875 876 pProfile->pszEnvironment = pEnvironment; 877 878 /* Return the other info */ 879 if (!GetTokenInformation(pgContext->UserToken, 880 TokenStatistics, 881 &Stats, 882 sizeof(Stats), 883 &cbStats)) 884 { 885 WARN("Couldn't get Authentication Id from user token!\n"); 886 goto cleanup; 887 } 888 889 *pgContext->pAuthenticationId = Stats.AuthenticationId; 890 pgContext->pMprNotifyInfo->pszUserName = DuplicateString(UserName); 891 pgContext->pMprNotifyInfo->pszDomain = DuplicateString(Domain); 892 pgContext->pMprNotifyInfo->pszPassword = DuplicateString(Password); 893 pgContext->pMprNotifyInfo->pszOldPassword = NULL; 894 *pgContext->pdwOptions = 0; 895 *pgContext->pProfile = pProfile; 896 return TRUE; 897 898cleanup: 899 if (pEnvironment) 900 LocalFree(pEnvironment); 901 if (pProfile) 902 LocalFree(pProfile); 903 return FALSE; 904} 905 906 907/* 908 * @implemented 909 */ 910VOID WINAPI 911WlxDisplaySASNotice( 912 IN PVOID pWlxContext) 913{ 914 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 915 916 TRACE("WlxDisplaySASNotice(%p)\n", pWlxContext); 917 918 if (GetSystemMetrics(SM_REMOTESESSION)) 919 { 920 /* User is remotely logged on. Don't display a notice */ 921 pgContext->pWlxFuncs->WlxSasNotify(pgContext->hWlx, WLX_SAS_TYPE_CTRL_ALT_DEL); 922 return; 923 } 924 925 if (pgContext->bAutoAdminLogon) 926 { 927 if (pgContext->bIgnoreShiftOverride || 928 (GetKeyState(VK_SHIFT) >= 0)) 929 { 930 /* Don't display the window, we want to do an automatic logon */ 931 pgContext->pWlxFuncs->WlxSasNotify(pgContext->hWlx, WLX_SAS_TYPE_CTRL_ALT_DEL); 932 return; 933 } 934 935 pgContext->bAutoAdminLogon = FALSE; 936 } 937 938 if (pgContext->bDisableCAD) 939 { 940 pgContext->pWlxFuncs->WlxSasNotify(pgContext->hWlx, WLX_SAS_TYPE_CTRL_ALT_DEL); 941 return; 942 } 943 944 pGinaUI->DisplaySASNotice(pgContext); 945 946 TRACE("WlxDisplaySASNotice() done\n"); 947} 948 949/* 950 * @implemented 951 */ 952INT WINAPI 953WlxLoggedOutSAS( 954 IN PVOID pWlxContext, 955 IN DWORD dwSasType, 956 OUT PLUID pAuthenticationId, 957 IN OUT PSID pLogonSid, 958 OUT PDWORD pdwOptions, 959 OUT PHANDLE phToken, 960 OUT PWLX_MPR_NOTIFY_INFO pMprNotifyInfo, 961 OUT PVOID *pProfile) 962{ 963 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 964 INT res; 965 966 TRACE("WlxLoggedOutSAS()\n"); 967 968 UNREFERENCED_PARAMETER(dwSasType); 969 UNREFERENCED_PARAMETER(pLogonSid); 970 971 pgContext->pAuthenticationId = pAuthenticationId; 972 pgContext->pdwOptions = pdwOptions; 973 pgContext->pMprNotifyInfo = pMprNotifyInfo; 974 pgContext->pProfile = pProfile; 975 976 res = pGinaUI->LoggedOutSAS(pgContext); 977 978 /* Return the logon information only if necessary */ 979 if (res == WLX_SAS_ACTION_LOGON) 980 *phToken = pgContext->UserToken; 981 982 return res; 983} 984 985/* 986 * @implemented 987 */ 988int WINAPI 989WlxWkstaLockedSAS( 990 PVOID pWlxContext, 991 DWORD dwSasType) 992{ 993 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 994 995 TRACE("WlxWkstaLockedSAS()\n"); 996 997 UNREFERENCED_PARAMETER(dwSasType); 998 999 return pGinaUI->LockedSAS(pgContext); 1000} 1001 1002 1003/* 1004 * @implemented 1005 */ 1006VOID 1007WINAPI 1008WlxDisplayLockedNotice(PVOID pWlxContext) 1009{ 1010 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 1011 1012 TRACE("WlxDisplayLockedNotice()\n"); 1013 1014 if (pgContext->bDisableCAD) 1015 { 1016 pgContext->pWlxFuncs->WlxSasNotify(pgContext->hWlx, WLX_SAS_TYPE_CTRL_ALT_DEL); 1017 return; 1018 } 1019 1020 pGinaUI->DisplayLockedNotice(pgContext); 1021} 1022 1023 1024/* 1025 * @implemented 1026 */ 1027BOOL WINAPI 1028WlxIsLogoffOk( 1029 PVOID pWlxContext) 1030{ 1031 TRACE("WlxIsLogoffOk()\n"); 1032 UNREFERENCED_PARAMETER(pWlxContext); 1033 return TRUE; 1034} 1035 1036 1037/* 1038 * @implemented 1039 */ 1040VOID WINAPI 1041WlxLogoff( 1042 PVOID pWlxContext) 1043{ 1044 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 1045 1046 TRACE("WlxLogoff(%p)\n", pWlxContext); 1047 1048 /* Reset the captured Winlogon pointers */ 1049 pgContext->pAuthenticationId = NULL; 1050 pgContext->pdwOptions = NULL; 1051 pgContext->pMprNotifyInfo = NULL; 1052 pgContext->pProfile = NULL; 1053 1054 /* 1055 * Reset user login information. 1056 * Keep pgContext->UserName and pgContext->DomainName around 1057 * if we want to show them as default (last logged user) in 1058 * the Log-On dialog. 1059 */ 1060 ZeroMemory(&pgContext->LogonTime, sizeof(pgContext->LogonTime)); 1061 1062 /* Delete the password */ 1063 SecureZeroMemory(pgContext->Password, sizeof(pgContext->Password)); 1064 1065 /* Close the user token */ 1066 CloseHandle(pgContext->UserToken); 1067 pgContext->UserToken = NULL; 1068} 1069 1070 1071/* 1072 * @implemented 1073 */ 1074VOID WINAPI 1075WlxShutdown( 1076 PVOID pWlxContext, 1077 DWORD ShutdownType) 1078{ 1079 PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; 1080 NTSTATUS Status; 1081 1082 TRACE("WlxShutdown(%p %lx)\n", pWlxContext, ShutdownType); 1083 1084 /* Close the LSA handle */ 1085 pgContext->AuthenticationPackage = 0; 1086 Status = LsaDeregisterLogonProcess(pgContext->LsaHandle); 1087 if (!NT_SUCCESS(Status)) 1088 { 1089 ERR("LsaDeregisterLogonProcess failed (Status 0x%08lx)\n", Status); 1090 } 1091} 1092 1093 1094BOOL WINAPI 1095DllMain( 1096 IN HINSTANCE hinstDLL, 1097 IN DWORD dwReason, 1098 IN LPVOID lpvReserved) 1099{ 1100 UNREFERENCED_PARAMETER(lpvReserved); 1101 1102 if (dwReason == DLL_PROCESS_ATTACH) 1103 { 1104 hDllInstance = hinstDLL; 1105 1106 RtlAllocateAndInitializeSid(&SystemAuthority, 1107 2, 1108 SECURITY_BUILTIN_DOMAIN_RID, 1109 DOMAIN_ALIAS_RID_ADMINS, 1110 SECURITY_NULL_RID, 1111 SECURITY_NULL_RID, 1112 SECURITY_NULL_RID, 1113 SECURITY_NULL_RID, 1114 SECURITY_NULL_RID, 1115 SECURITY_NULL_RID, 1116 &AdminSid); 1117 1118 } 1119 else if (dwReason == DLL_PROCESS_DETACH) 1120 { 1121 if (AdminSid != NULL) 1122 RtlFreeSid(AdminSid); 1123 } 1124 1125 return TRUE; 1126}