xref: /reactos/dll/win32/msgina/msgina.c (revision c2c66aff)
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 
33 HINSTANCE hDllInstance;
34 
35 extern GINA_UI GinaGraphicalUI;
36 extern GINA_UI GinaTextUI;
37 static PGINA_UI pGinaUI;
38 static SID_IDENTIFIER_AUTHORITY SystemAuthority = {SECURITY_NT_AUTHORITY};
39 static PSID AdminSid;
40 
41 /*
42  * @implemented
43  */
44 BOOL WINAPI
45 WlxNegotiate(
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 
59 LONG
60 ReadRegSzValue(
61     IN HKEY hKey,
62     IN LPCWSTR pszValue,
63     OUT LPWSTR* pValue)
64 {
65     LONG rc;
66     DWORD dwType;
67     DWORD cbData = 0;
68     LPWSTR Value;
69 
70     if (!pValue)
71         return ERROR_INVALID_PARAMETER;
72 
73     *pValue = NULL;
74     rc = RegQueryValueExW(hKey, pszValue, NULL, &dwType, NULL, &cbData);
75     if (rc != ERROR_SUCCESS)
76         return rc;
77     if (dwType != REG_SZ)
78         return ERROR_FILE_NOT_FOUND;
79     Value = HeapAlloc(GetProcessHeap(), 0, cbData + sizeof(WCHAR));
80     if (!Value)
81         return ERROR_NOT_ENOUGH_MEMORY;
82     rc = RegQueryValueExW(hKey, pszValue, NULL, NULL, (LPBYTE)Value, &cbData);
83     if (rc != ERROR_SUCCESS)
84     {
85         HeapFree(GetProcessHeap(), 0, Value);
86         return rc;
87     }
88     /* NULL-terminate the string */
89     Value[cbData / sizeof(WCHAR)] = '\0';
90 
91     *pValue = Value;
92     return ERROR_SUCCESS;
93 }
94 
95 static LONG
96 ReadRegDwordValue(
97     IN HKEY hKey,
98     IN LPCWSTR pszValue,
99     OUT LPDWORD pValue)
100 {
101     LONG rc;
102     DWORD dwType;
103     DWORD cbData;
104     DWORD dwValue;
105 
106     if (!pValue)
107         return ERROR_INVALID_PARAMETER;
108 
109     cbData = sizeof(DWORD);
110     rc = RegQueryValueExW(hKey, pszValue, NULL, &dwType, (LPBYTE)&dwValue, &cbData);
111     if (rc == ERROR_SUCCESS && dwType == REG_DWORD)
112         *pValue = dwValue;
113 
114     return ERROR_SUCCESS;
115 }
116 
117 static VOID
118 ChooseGinaUI(VOID)
119 {
120     HKEY ControlKey = NULL;
121     LPWSTR SystemStartOptions = NULL;
122     LPWSTR CurrentOption, NextOption; /* Pointers into SystemStartOptions */
123     BOOL ConsoleBoot = FALSE;
124     LONG rc;
125 
126     rc = RegOpenKeyExW(
127         HKEY_LOCAL_MACHINE,
128         L"SYSTEM\\CurrentControlSet\\Control",
129         0,
130         KEY_QUERY_VALUE,
131         &ControlKey);
132 
133     rc = ReadRegSzValue(ControlKey, L"SystemStartOptions", &SystemStartOptions);
134     if (rc != ERROR_SUCCESS)
135         goto cleanup;
136 
137     /* Check for CONSOLE switch in SystemStartOptions */
138     CurrentOption = SystemStartOptions;
139     while (CurrentOption)
140     {
141         NextOption = wcschr(CurrentOption, L' ');
142         if (NextOption)
143             *NextOption = L'\0';
144         if (wcsicmp(CurrentOption, L"CONSOLE") == 0)
145         {
146             TRACE("Found %S. Switching to console boot\n", CurrentOption);
147             ConsoleBoot = TRUE;
148             goto cleanup;
149         }
150         CurrentOption = NextOption ? NextOption + 1 : NULL;
151     }
152 
153 cleanup:
154     if (ConsoleBoot)
155         pGinaUI = &GinaTextUI;
156     else
157         pGinaUI = &GinaGraphicalUI;
158 
159     if (ControlKey != NULL)
160         RegCloseKey(ControlKey);
161     HeapFree(GetProcessHeap(), 0, SystemStartOptions);
162 }
163 
164 
165 static
166 BOOL
167 GetRegistrySettings(PGINA_CONTEXT pgContext)
168 {
169     HKEY hKey = NULL;
170     LPWSTR lpAutoAdminLogon = NULL;
171     LPWSTR lpDontDisplayLastUserName = NULL;
172     LPWSTR lpShutdownWithoutLogon = NULL;
173     DWORD dwDisableCAD = 0;
174     DWORD dwSize;
175     LONG rc;
176 
177     rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
178                        L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
179                        0,
180                        KEY_QUERY_VALUE,
181                        &hKey);
182     if (rc != ERROR_SUCCESS)
183     {
184         WARN("RegOpenKeyExW() failed with error %lu\n", rc);
185         return FALSE;
186     }
187 
188     rc = ReadRegSzValue(hKey,
189                         L"AutoAdminLogon",
190                         &lpAutoAdminLogon);
191     if (rc == ERROR_SUCCESS)
192     {
193         if (wcscmp(lpAutoAdminLogon, L"1") == 0)
194             pgContext->bAutoAdminLogon = TRUE;
195     }
196 
197     TRACE("bAutoAdminLogon: %s\n", pgContext->bAutoAdminLogon ? "TRUE" : "FALSE");
198 
199     rc = ReadRegDwordValue(hKey,
200                            L"DisableCAD",
201                            &dwDisableCAD);
202     if (rc == ERROR_SUCCESS)
203     {
204         if (dwDisableCAD != 0)
205             pgContext->bDisableCAD = TRUE;
206     }
207 
208     TRACE("bDisableCAD: %s\n", pgContext->bDisableCAD ? "TRUE" : "FALSE");
209 
210     pgContext->bShutdownWithoutLogon = TRUE;
211     rc = ReadRegSzValue(hKey,
212                         L"ShutdownWithoutLogon",
213                         &lpShutdownWithoutLogon);
214     if (rc == ERROR_SUCCESS)
215     {
216         if (wcscmp(lpShutdownWithoutLogon, L"0") == 0)
217             pgContext->bShutdownWithoutLogon = FALSE;
218     }
219 
220     rc = ReadRegSzValue(hKey,
221                         L"DontDisplayLastUserName",
222                         &lpDontDisplayLastUserName);
223     if (rc == ERROR_SUCCESS)
224     {
225         if (wcscmp(lpDontDisplayLastUserName, L"1") == 0)
226             pgContext->bDontDisplayLastUserName = TRUE;
227     }
228 
229     dwSize = sizeof(pgContext->UserName);
230     rc = RegQueryValueExW(hKey,
231                           L"DefaultUserName",
232                           NULL,
233                           NULL,
234                           (LPBYTE)&pgContext->UserName,
235                           &dwSize);
236 
237     dwSize = sizeof(pgContext->Domain);
238     rc = RegQueryValueExW(hKey,
239                           L"DefaultDomain",
240                           NULL,
241                           NULL,
242                           (LPBYTE)&pgContext->Domain,
243                           &dwSize);
244 
245     if (lpShutdownWithoutLogon != NULL)
246         HeapFree(GetProcessHeap(), 0, lpShutdownWithoutLogon);
247 
248     if (lpDontDisplayLastUserName != NULL)
249         HeapFree(GetProcessHeap(), 0, lpDontDisplayLastUserName);
250 
251     if (lpAutoAdminLogon != NULL)
252         HeapFree(GetProcessHeap(), 0, lpAutoAdminLogon);
253 
254     if (hKey != NULL)
255         RegCloseKey(hKey);
256 
257     return TRUE;
258 }
259 
260 typedef DWORD (WINAPI *pThemeWait)(DWORD dwTimeout);
261 typedef BOOL (WINAPI *pThemeWatch)(void);
262 
263 static void
264 InitThemeSupport(VOID)
265 {
266     HMODULE hDll = LoadLibraryW(L"shsvcs.dll");
267     pThemeWait themeWait;
268     pThemeWatch themeWatch;
269 
270     if(!hDll)
271         return;
272 
273     themeWait = (pThemeWait) GetProcAddress(hDll, (LPCSTR)2);
274     themeWatch = (pThemeWatch) GetProcAddress(hDll, (LPCSTR)1);
275 
276     if(themeWait && themeWatch)
277     {
278         themeWait(5000);
279         themeWatch();
280     }
281 }
282 
283 /*
284  * @implemented
285  */
286 BOOL WINAPI
287 WlxInitialize(
288     LPWSTR lpWinsta,
289     HANDLE hWlx,
290     PVOID  pvReserved,
291     PVOID  pWinlogonFunctions,
292     PVOID  *pWlxContext)
293 {
294     PGINA_CONTEXT pgContext;
295 
296     UNREFERENCED_PARAMETER(pvReserved);
297 
298     InitThemeSupport();
299 
300     pgContext = (PGINA_CONTEXT)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(GINA_CONTEXT));
301     if(!pgContext)
302     {
303         WARN("LocalAlloc() failed\n");
304         return FALSE;
305     }
306 
307     if (!GetRegistrySettings(pgContext))
308     {
309         WARN("GetRegistrySettings() failed\n");
310         LocalFree(pgContext);
311         return FALSE;
312     }
313 
314     /* Return the context to winlogon */
315     *pWlxContext = (PVOID)pgContext;
316     pgContext->hDllInstance = hDllInstance;
317 
318     /* Save pointer to dispatch table */
319     pgContext->pWlxFuncs = (PWLX_DISPATCH_VERSION_1_3)pWinlogonFunctions;
320 
321     /* Save the winlogon handle used to call the dispatch functions */
322     pgContext->hWlx = hWlx;
323 
324     /* Save window station */
325     pgContext->station = lpWinsta;
326 
327     /* Clear status window handle */
328     pgContext->hStatusWindow = NULL;
329 
330     /* Notify winlogon that we will use the default SAS */
331     pgContext->pWlxFuncs->WlxUseCtrlAltDel(hWlx);
332 
333     /* Locates the authentication package */
334     //LsaRegisterLogonProcess(...);
335 
336     /* Check autologon settings the first time */
337     pgContext->AutoLogonState = AUTOLOGON_CHECK_REGISTRY;
338 
339     pgContext->nShutdownAction = WLX_SAS_ACTION_SHUTDOWN_POWER_OFF;
340 
341     ChooseGinaUI();
342     return pGinaUI->Initialize(pgContext);
343 }
344 
345 /*
346  * @implemented
347  */
348 BOOL
349 WINAPI
350 WlxScreenSaverNotify(
351     PVOID pWlxContext,
352     BOOL  *pSecure)
353 {
354 #if 0
355     PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
356     WCHAR szBuffer[2];
357     HKEY hKeyCurrentUser, hKey;
358     DWORD bufferSize = sizeof(szBuffer);
359     DWORD varType = REG_SZ;
360     LONG rc;
361 
362     TRACE("(%p %p)\n", pWlxContext, pSecure);
363 
364     *pSecure = TRUE;
365 
366     /*
367      * Policy setting:
368      *    HKLM\Software\Policies\Microsoft\Windows\Control Panel\Desktop : ScreenSaverIsSecure
369      * User setting:
370      *    HKCU\Control Panel\Desktop : ScreenSaverIsSecure
371      */
372 
373     if (!ImpersonateLoggedOnUser(pgContext->UserToken))
374     {
375         ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
376         *pSecure = FALSE;
377         return TRUE;
378     }
379 
380     /* Open the current user HKCU key */
381     rc = RegOpenCurrentUser(MAXIMUM_ALLOWED, &hKeyCurrentUser);
382     TRACE("RegOpenCurrentUser: %ld\n", rc);
383     if (rc == ERROR_SUCCESS)
384     {
385         /* Open the subkey */
386         rc = RegOpenKeyExW(hKeyCurrentUser,
387                            L"Control Panel\\Desktop",
388                            0,
389                            KEY_QUERY_VALUE,
390                            &hKey);
391         TRACE("RegOpenKeyExW: %ld\n", rc);
392         RegCloseKey(hKeyCurrentUser);
393     }
394 
395     /* Read the value */
396     if (rc == ERROR_SUCCESS)
397     {
398         rc = RegQueryValueExW(hKey,
399                               L"ScreenSaverIsSecure",
400                               NULL,
401                               &varType,
402                               (LPBYTE)szBuffer,
403                               &bufferSize);
404 
405         TRACE("RegQueryValueExW: %ld\n", rc);
406 
407         if (rc == ERROR_SUCCESS)
408         {
409             TRACE("szBuffer: \"%S\"\n", szBuffer);
410             *pSecure = _wtoi(szBuffer);
411         }
412 
413         RegCloseKey(hKey);
414     }
415 
416     /* Revert the impersonation */
417     RevertToSelf();
418 
419     TRACE("*pSecure: %ld\n", *pSecure);
420 #endif
421 
422     *pSecure = FALSE;
423 
424     return TRUE;
425 }
426 
427 /*
428  * @implemented
429  */
430 BOOL WINAPI
431 WlxStartApplication(
432     PVOID pWlxContext,
433     PWSTR pszDesktopName,
434     PVOID pEnvironment,
435     PWSTR pszCmdLine)
436 {
437     PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
438     STARTUPINFOW StartupInfo;
439     PROCESS_INFORMATION ProcessInformation;
440     WCHAR CurrentDirectory[MAX_PATH];
441     HANDLE hAppToken;
442     UINT len;
443     BOOL ret;
444 
445     len = GetWindowsDirectoryW(CurrentDirectory, MAX_PATH);
446     if (len == 0 || len > MAX_PATH)
447     {
448         ERR("GetWindowsDirectoryW() failed\n");
449         return FALSE;
450     }
451 
452     ret = DuplicateTokenEx(pgContext->UserToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &hAppToken);
453     if (!ret)
454     {
455         ERR("DuplicateTokenEx() failed with error %lu\n", GetLastError());
456         return FALSE;
457     }
458 
459     ZeroMemory(&StartupInfo, sizeof(StartupInfo));
460     ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
461     StartupInfo.cb = sizeof(StartupInfo);
462     StartupInfo.lpTitle = pszCmdLine;
463     StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
464     StartupInfo.wShowWindow = SW_SHOW;
465     StartupInfo.lpDesktop = pszDesktopName;
466 
467     len = GetWindowsDirectoryW(CurrentDirectory, MAX_PATH);
468     if (len == 0 || len > MAX_PATH)
469     {
470         ERR("GetWindowsDirectoryW() failed\n");
471         return FALSE;
472     }
473     ret = CreateProcessAsUserW(
474         hAppToken,
475         pszCmdLine,
476         NULL,
477         NULL,
478         NULL,
479         FALSE,
480         CREATE_UNICODE_ENVIRONMENT,
481         pEnvironment,
482         CurrentDirectory,
483         &StartupInfo,
484         &ProcessInformation);
485     CloseHandle(ProcessInformation.hProcess);
486     CloseHandle(ProcessInformation.hThread);
487     CloseHandle(hAppToken);
488     if (!ret)
489         ERR("CreateProcessAsUserW() failed with error %lu\n", GetLastError());
490     return ret;
491 }
492 
493 /*
494  * @implemented
495  */
496 BOOL WINAPI
497 WlxActivateUserShell(
498     PVOID pWlxContext,
499     PWSTR pszDesktopName,
500     PWSTR pszMprLogonScript,
501     PVOID pEnvironment)
502 {
503     HKEY hKey;
504     DWORD BufSize, ValueType;
505     WCHAR pszUserInitApp[MAX_PATH + 1];
506     WCHAR pszExpUserInitApp[MAX_PATH];
507     DWORD len;
508     LONG rc;
509 
510     TRACE("WlxActivateUserShell()\n");
511 
512     UNREFERENCED_PARAMETER(pszMprLogonScript);
513 
514     /* Get the path of userinit */
515     rc = RegOpenKeyExW(
516         HKEY_LOCAL_MACHINE,
517         L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
518         0,
519         KEY_QUERY_VALUE,
520         &hKey);
521     if (rc != ERROR_SUCCESS)
522     {
523         WARN("RegOpenKeyExW() failed with error %lu\n", rc);
524         return FALSE;
525     }
526 
527     /* Query userinit application */
528     BufSize = sizeof(pszUserInitApp) - sizeof(UNICODE_NULL);
529     rc = RegQueryValueExW(
530         hKey,
531         L"Userinit",
532         NULL,
533         &ValueType,
534         (LPBYTE)pszUserInitApp,
535         &BufSize);
536     RegCloseKey(hKey);
537     if (rc != ERROR_SUCCESS || (ValueType != REG_SZ && ValueType != REG_EXPAND_SZ))
538     {
539         WARN("RegQueryValueExW() failed with error %lu\n", rc);
540         return FALSE;
541     }
542     pszUserInitApp[MAX_PATH] = UNICODE_NULL;
543 
544     len = ExpandEnvironmentStringsW(pszUserInitApp, pszExpUserInitApp, MAX_PATH);
545     if (len > MAX_PATH)
546     {
547         WARN("ExpandEnvironmentStringsW() failed. Required size %lu\n", len);
548         return FALSE;
549     }
550 
551     /* Start userinit app */
552     return WlxStartApplication(pWlxContext, pszDesktopName, pEnvironment, pszExpUserInitApp);
553 }
554 
555 /*
556  * @implemented
557  */
558 int WINAPI
559 WlxLoggedOnSAS(
560     PVOID pWlxContext,
561     DWORD dwSasType,
562     PVOID pReserved)
563 {
564     PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
565     INT SasAction = WLX_SAS_ACTION_NONE;
566 
567     TRACE("WlxLoggedOnSAS(0x%lx)\n", dwSasType);
568 
569     UNREFERENCED_PARAMETER(pReserved);
570 
571     switch (dwSasType)
572     {
573         case WLX_SAS_TYPE_CTRL_ALT_DEL:
574         case WLX_SAS_TYPE_TIMEOUT:
575         {
576             SasAction = pGinaUI->LoggedOnSAS(pgContext, dwSasType);
577             break;
578         }
579         case WLX_SAS_TYPE_SC_INSERT:
580         {
581             FIXME("WlxLoggedOnSAS: SasType WLX_SAS_TYPE_SC_INSERT not supported!\n");
582             break;
583         }
584         case WLX_SAS_TYPE_SC_REMOVE:
585         {
586             FIXME("WlxLoggedOnSAS: SasType WLX_SAS_TYPE_SC_REMOVE not supported!\n");
587             break;
588         }
589         default:
590         {
591             WARN("WlxLoggedOnSAS: Unknown SasType: 0x%x\n", dwSasType);
592             break;
593         }
594     }
595 
596     return SasAction;
597 }
598 
599 /*
600  * @implemented
601  */
602 BOOL WINAPI
603 WlxDisplayStatusMessage(
604     IN PVOID pWlxContext,
605     IN HDESK hDesktop,
606     IN DWORD dwOptions,
607     IN PWSTR pTitle,
608     IN PWSTR pMessage)
609 {
610     PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
611 
612     TRACE("WlxDisplayStatusMessage(\"%S\")\n", pMessage);
613 
614     return pGinaUI->DisplayStatusMessage(pgContext, hDesktop, dwOptions, pTitle, pMessage);
615 }
616 
617 /*
618  * @implemented
619  */
620 BOOL WINAPI
621 WlxRemoveStatusMessage(
622     IN PVOID pWlxContext)
623 {
624     PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
625 
626     TRACE("WlxRemoveStatusMessage()\n");
627 
628     return pGinaUI->RemoveStatusMessage(pgContext);
629 }
630 
631 static PWSTR
632 DuplicationString(PWSTR Str)
633 {
634     DWORD cb;
635     PWSTR NewStr;
636 
637     if (Str == NULL) return NULL;
638 
639     cb = (wcslen(Str) + 1) * sizeof(WCHAR);
640     if ((NewStr = LocalAlloc(LMEM_FIXED, cb)))
641         memcpy(NewStr, Str, cb);
642     return NewStr;
643 }
644 
645 
646 BOOL
647 DoAdminUnlock(
648     IN PGINA_CONTEXT pgContext,
649     IN PWSTR UserName,
650     IN PWSTR Domain,
651     IN PWSTR Password)
652 {
653     HANDLE hToken = NULL;
654     PTOKEN_GROUPS Groups = NULL;
655     BOOL bIsAdmin = FALSE;
656     ULONG Size;
657     ULONG i;
658     NTSTATUS Status;
659     NTSTATUS SubStatus = STATUS_SUCCESS;
660 
661     TRACE("(%S %S %S)\n", UserName, Domain, Password);
662 
663     Status = ConnectToLsa(pgContext);
664     if (!NT_SUCCESS(Status))
665     {
666         WARN("ConnectToLsa() failed\n");
667         return FALSE;
668     }
669 
670     Status = MyLogonUser(pgContext->LsaHandle,
671                          pgContext->AuthenticationPackage,
672                          UserName,
673                          Domain,
674                          Password,
675                          &pgContext->UserToken,
676                          &SubStatus);
677     if (!NT_SUCCESS(Status))
678     {
679         WARN("MyLogonUser() failed\n");
680         return FALSE;
681     }
682 
683     Status = NtQueryInformationToken(hToken,
684                                      TokenGroups,
685                                      NULL,
686                                      0,
687                                      &Size);
688     if ((Status != STATUS_SUCCESS) && (Status != STATUS_BUFFER_TOO_SMALL))
689     {
690         TRACE("NtQueryInformationToken() failed (Status 0x%08lx)\n", Status);
691         goto done;
692     }
693 
694     Groups = HeapAlloc(GetProcessHeap(), 0, Size);
695     if (Groups == NULL)
696     {
697         TRACE("HeapAlloc() failed\n");
698         goto done;
699     }
700 
701     Status = NtQueryInformationToken(hToken,
702                                      TokenGroups,
703                                      Groups,
704                                      Size,
705                                      &Size);
706     if (!NT_SUCCESS(Status))
707     {
708         TRACE("NtQueryInformationToken() failed (Status 0x%08lx)\n", Status);
709         goto done;
710     }
711 
712     for (i = 0; i < Groups->GroupCount; i++)
713     {
714         if (RtlEqualSid(Groups->Groups[i].Sid, AdminSid))
715         {
716             TRACE("Member of Admins group\n");
717             bIsAdmin = TRUE;
718             break;
719         }
720     }
721 
722 done:
723     if (Groups != NULL)
724         HeapFree(GetProcessHeap(), 0, Groups);
725 
726     if (hToken != NULL)
727         CloseHandle(hToken);
728 
729     return bIsAdmin;
730 }
731 
732 
733 NTSTATUS
734 DoLoginTasks(
735     IN OUT PGINA_CONTEXT pgContext,
736     IN PWSTR UserName,
737     IN PWSTR Domain,
738     IN PWSTR Password,
739     OUT PNTSTATUS SubStatus)
740 {
741     NTSTATUS Status;
742 
743     Status = ConnectToLsa(pgContext);
744     if (!NT_SUCCESS(Status))
745     {
746         WARN("ConnectToLsa() failed (Status 0x%08lx)\n", Status);
747         return Status;
748     }
749 
750     Status = MyLogonUser(pgContext->LsaHandle,
751                          pgContext->AuthenticationPackage,
752                          UserName,
753                          Domain,
754                          Password,
755                          &pgContext->UserToken,
756                          SubStatus);
757     if (!NT_SUCCESS(Status))
758     {
759         WARN("MyLogonUser() failed (Status 0x%08lx)\n", Status);
760     }
761 
762     return Status;
763 }
764 
765 
766 BOOL
767 CreateProfile(
768     IN OUT PGINA_CONTEXT pgContext,
769     IN PWSTR UserName,
770     IN PWSTR Domain,
771     IN PWSTR Password)
772 {
773     LPWSTR ProfilePath = NULL;
774     LPWSTR lpEnvironment = NULL;
775     TOKEN_STATISTICS Stats;
776     PWLX_PROFILE_V2_0 pProfile = NULL;
777     DWORD cbStats, cbSize;
778     DWORD dwLength;
779     BOOL bResult;
780 
781     /* Store the logon time in the context */
782     GetLocalTime(&pgContext->LogonTime);
783 
784     /* Store user and domain in the context */
785     wcscpy(pgContext->UserName, UserName);
786     if (Domain == NULL || wcslen(Domain) == 0)
787     {
788         dwLength = _countof(pgContext->Domain);
789         GetComputerNameW(pgContext->Domain, &dwLength);
790     }
791     else
792     {
793         wcscpy(pgContext->Domain, Domain);
794     }
795 
796     /* Get profile path */
797     cbSize = 0;
798     bResult = GetProfilesDirectoryW(NULL, &cbSize);
799     if (!bResult && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
800     {
801         ProfilePath = HeapAlloc(GetProcessHeap(), 0, cbSize * sizeof(WCHAR));
802         if (!ProfilePath)
803         {
804             WARN("HeapAlloc() failed\n");
805             goto cleanup;
806         }
807         bResult = GetProfilesDirectoryW(ProfilePath, &cbSize);
808     }
809     if (!bResult)
810     {
811         WARN("GetUserProfileDirectoryW() failed\n");
812         goto cleanup;
813     }
814 
815     /* Allocate memory for profile */
816     pProfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WLX_PROFILE_V2_0));
817     if (!pProfile)
818     {
819         WARN("HeapAlloc() failed\n");
820         goto cleanup;
821     }
822     pProfile->dwType = WLX_PROFILE_TYPE_V2_0;
823     pProfile->pszProfile = ProfilePath;
824 
825     cbSize = sizeof(L"LOGONSERVER=\\\\") +
826              wcslen(pgContext->Domain) * sizeof(WCHAR) +
827              sizeof(UNICODE_NULL);
828     lpEnvironment = HeapAlloc(GetProcessHeap(), 0, cbSize);
829     if (!lpEnvironment)
830     {
831         WARN("HeapAlloc() failed\n");
832         goto cleanup;
833     }
834 
835     StringCbPrintfW(lpEnvironment, cbSize, L"LOGONSERVER=\\\\%ls", pgContext->Domain);
836     ASSERT(wcslen(lpEnvironment) == cbSize / sizeof(WCHAR) - 2);
837     lpEnvironment[cbSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
838 
839     pProfile->pszEnvironment = lpEnvironment;
840 
841     if (!GetTokenInformation(pgContext->UserToken,
842                              TokenStatistics,
843                              &Stats,
844                              sizeof(Stats),
845                              &cbStats))
846     {
847         WARN("Couldn't get Authentication id from user token!\n");
848         goto cleanup;
849     }
850 
851     *pgContext->pAuthenticationId = Stats.AuthenticationId;
852     pgContext->pMprNotifyInfo->pszUserName = DuplicationString(UserName);
853     pgContext->pMprNotifyInfo->pszDomain = DuplicationString(Domain);
854     pgContext->pMprNotifyInfo->pszPassword = DuplicationString(Password);
855     pgContext->pMprNotifyInfo->pszOldPassword = NULL;
856     *pgContext->pdwOptions = 0;
857     *pgContext->pProfile = pProfile;
858     return TRUE;
859 
860 cleanup:
861     if (pProfile)
862     {
863         HeapFree(GetProcessHeap(), 0, pProfile->pszEnvironment);
864     }
865     HeapFree(GetProcessHeap(), 0, pProfile);
866     HeapFree(GetProcessHeap(), 0, ProfilePath);
867     return FALSE;
868 }
869 
870 
871 static BOOL
872 DoAutoLogon(
873     IN PGINA_CONTEXT pgContext)
874 {
875     HKEY WinLogonKey = NULL;
876     LPWSTR AutoLogon = NULL;
877     LPWSTR AutoCount = NULL;
878     LPWSTR IgnoreShiftOverride = NULL;
879     LPWSTR UserName = NULL;
880     LPWSTR Domain = NULL;
881     LPWSTR Password = NULL;
882     BOOL result = FALSE;
883     LONG rc;
884     NTSTATUS Status;
885     NTSTATUS SubStatus = STATUS_SUCCESS;
886 
887     TRACE("DoAutoLogon(): AutoLogonState = %lu\n",
888         pgContext->AutoLogonState);
889 
890     if (pgContext->AutoLogonState == AUTOLOGON_DISABLED)
891         return FALSE;
892 
893     rc = RegOpenKeyExW(
894         HKEY_LOCAL_MACHINE,
895         L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon",
896         0,
897         KEY_QUERY_VALUE,
898         &WinLogonKey);
899     if (rc != ERROR_SUCCESS)
900         goto cleanup;
901 
902     if (pgContext->AutoLogonState == AUTOLOGON_CHECK_REGISTRY)
903     {
904         /* Set it by default to disabled, we might reenable it again later */
905         pgContext->AutoLogonState = AUTOLOGON_DISABLED;
906 
907         rc = ReadRegSzValue(WinLogonKey, L"AutoAdminLogon", &AutoLogon);
908         if (rc != ERROR_SUCCESS)
909             goto cleanup;
910         if (wcscmp(AutoLogon, L"1") != 0)
911             goto cleanup;
912 
913         rc = ReadRegSzValue(WinLogonKey, L"AutoLogonCount", &AutoCount);
914         if (rc == ERROR_SUCCESS && wcscmp(AutoCount, L"0") == 0)
915             goto cleanup;
916         else if (rc != ERROR_FILE_NOT_FOUND)
917             goto cleanup;
918 
919         rc = ReadRegSzValue(WinLogonKey, L"IgnoreShiftOverride", &UserName);
920         if (rc == ERROR_SUCCESS)
921         {
922             if (wcscmp(AutoLogon, L"1") != 0 && GetKeyState(VK_SHIFT) < 0)
923                 goto cleanup;
924         }
925         else if (GetKeyState(VK_SHIFT) < 0)
926         {
927             /* User pressed SHIFT */
928             goto cleanup;
929         }
930 
931         pgContext->AutoLogonState = AUTOLOGON_ONCE;
932         result = TRUE;
933     }
934     else /* pgContext->AutoLogonState == AUTOLOGON_ONCE */
935     {
936         pgContext->AutoLogonState = AUTOLOGON_DISABLED;
937 
938         rc = ReadRegSzValue(WinLogonKey, L"DefaultUserName", &UserName);
939         if (rc != ERROR_SUCCESS)
940             goto cleanup;
941         rc = ReadRegSzValue(WinLogonKey, L"DefaultDomain", &Domain);
942         if (rc != ERROR_SUCCESS && rc != ERROR_FILE_NOT_FOUND)
943             goto cleanup;
944         rc = ReadRegSzValue(WinLogonKey, L"DefaultPassword", &Password);
945         if (rc != ERROR_SUCCESS)
946             goto cleanup;
947 
948         Status = DoLoginTasks(pgContext, UserName, Domain, Password, &SubStatus);
949         if (!NT_SUCCESS(Status))
950         {
951             /* FIXME: Handle errors!!! */
952             result = FALSE;
953             goto cleanup;
954         }
955 
956         result = CreateProfile(pgContext, UserName, Domain, Password);
957         if (result)
958         {
959             ZeroMemory(pgContext->Password, sizeof(pgContext->Password));
960             wcscpy(pgContext->Password, Password);
961 
962             NotifyBootConfigStatus(TRUE);
963         }
964     }
965 
966 cleanup:
967     if (WinLogonKey != NULL)
968         RegCloseKey(WinLogonKey);
969     HeapFree(GetProcessHeap(), 0, AutoLogon);
970     HeapFree(GetProcessHeap(), 0, AutoCount);
971     HeapFree(GetProcessHeap(), 0, IgnoreShiftOverride);
972     HeapFree(GetProcessHeap(), 0, UserName);
973     HeapFree(GetProcessHeap(), 0, Domain);
974     HeapFree(GetProcessHeap(), 0, Password);
975     TRACE("DoAutoLogon(): AutoLogonState = %lu, returning %d\n",
976         pgContext->AutoLogonState, result);
977     return result;
978 }
979 
980 /*
981  * @implemented
982  */
983 VOID WINAPI
984 WlxDisplaySASNotice(
985     IN PVOID pWlxContext)
986 {
987     PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
988 
989     TRACE("WlxDisplaySASNotice(%p)\n", pWlxContext);
990 
991     if (GetSystemMetrics(SM_REMOTESESSION))
992     {
993         /* User is remotely logged on. Don't display a notice */
994         pgContext->pWlxFuncs->WlxSasNotify(pgContext->hWlx, WLX_SAS_TYPE_CTRL_ALT_DEL);
995         return;
996     }
997 
998     if (pgContext->bAutoAdminLogon)
999     {
1000         /* Don't display the window, we want to do an automatic logon */
1001         pgContext->AutoLogonState = AUTOLOGON_ONCE;
1002         pgContext->pWlxFuncs->WlxSasNotify(pgContext->hWlx, WLX_SAS_TYPE_CTRL_ALT_DEL);
1003         return;
1004     }
1005     else
1006         pgContext->AutoLogonState = AUTOLOGON_DISABLED;
1007 
1008     if (pgContext->bDisableCAD)
1009     {
1010         pgContext->pWlxFuncs->WlxSasNotify(pgContext->hWlx, WLX_SAS_TYPE_CTRL_ALT_DEL);
1011         return;
1012     }
1013 
1014     pGinaUI->DisplaySASNotice(pgContext);
1015 
1016     TRACE("WlxDisplaySASNotice() done\n");
1017 }
1018 
1019 /*
1020  * @implemented
1021  */
1022 INT WINAPI
1023 WlxLoggedOutSAS(
1024     IN PVOID pWlxContext,
1025     IN DWORD dwSasType,
1026     OUT PLUID pAuthenticationId,
1027     IN OUT PSID pLogonSid,
1028     OUT PDWORD pdwOptions,
1029     OUT PHANDLE phToken,
1030     OUT PWLX_MPR_NOTIFY_INFO pMprNotifyInfo,
1031     OUT PVOID *pProfile)
1032 {
1033     PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
1034     INT res;
1035 
1036     TRACE("WlxLoggedOutSAS()\n");
1037 
1038     UNREFERENCED_PARAMETER(dwSasType);
1039     UNREFERENCED_PARAMETER(pLogonSid);
1040 
1041     pgContext->pAuthenticationId = pAuthenticationId;
1042     pgContext->pdwOptions = pdwOptions;
1043     pgContext->pMprNotifyInfo = pMprNotifyInfo;
1044     pgContext->pProfile = pProfile;
1045 
1046     if (0 == GetSystemMetrics(SM_REMOTESESSION) &&
1047         DoAutoLogon(pgContext))
1048     {
1049         /* User is local and registry contains information
1050          * to log on him automatically */
1051         *phToken = pgContext->UserToken;
1052         return WLX_SAS_ACTION_LOGON;
1053     }
1054 
1055     res = pGinaUI->LoggedOutSAS(pgContext);
1056     *phToken = pgContext->UserToken;
1057     return res;
1058 }
1059 
1060 /*
1061  * @implemented
1062  */
1063 int WINAPI
1064 WlxWkstaLockedSAS(
1065     PVOID pWlxContext,
1066     DWORD dwSasType)
1067 {
1068     PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
1069 
1070     TRACE("WlxWkstaLockedSAS()\n");
1071 
1072     UNREFERENCED_PARAMETER(dwSasType);
1073 
1074     return pGinaUI->LockedSAS(pgContext);
1075 }
1076 
1077 
1078 /*
1079  * @implemented
1080  */
1081 VOID
1082 WINAPI
1083 WlxDisplayLockedNotice(PVOID pWlxContext)
1084 {
1085     PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
1086 
1087     TRACE("WlxDisplayLockedNotice()\n");
1088 
1089     if (pgContext->bDisableCAD)
1090     {
1091         pgContext->pWlxFuncs->WlxSasNotify(pgContext->hWlx, WLX_SAS_TYPE_CTRL_ALT_DEL);
1092         return;
1093     }
1094 
1095     pGinaUI->DisplayLockedNotice(pgContext);
1096 }
1097 
1098 
1099 /*
1100  * @implemented
1101  */
1102 BOOL WINAPI
1103 WlxIsLogoffOk(
1104     PVOID pWlxContext)
1105 {
1106     TRACE("WlxIsLogoffOk()\n");
1107     UNREFERENCED_PARAMETER(pWlxContext);
1108     return TRUE;
1109 }
1110 
1111 BOOL WINAPI
1112 DllMain(
1113     IN HINSTANCE hinstDLL,
1114     IN DWORD dwReason,
1115     IN LPVOID lpvReserved)
1116 {
1117     UNREFERENCED_PARAMETER(lpvReserved);
1118 
1119     if (dwReason == DLL_PROCESS_ATTACH)
1120     {
1121         hDllInstance = hinstDLL;
1122 
1123         RtlAllocateAndInitializeSid(&SystemAuthority,
1124                                     2,
1125                                     SECURITY_BUILTIN_DOMAIN_RID,
1126                                     DOMAIN_ALIAS_RID_ADMINS,
1127                                     SECURITY_NULL_RID,
1128                                     SECURITY_NULL_RID,
1129                                     SECURITY_NULL_RID,
1130                                     SECURITY_NULL_RID,
1131                                     SECURITY_NULL_RID,
1132                                     SECURITY_NULL_RID,
1133                                     &AdminSid);
1134 
1135     }
1136     else if (dwReason == DLL_PROCESS_DETACH)
1137     {
1138         if (AdminSid != NULL)
1139             RtlFreeSid(AdminSid);
1140     }
1141 
1142     return TRUE;
1143 }
1144