Reactos
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}