xref: /reactos/base/system/winlogon/winlogon.c (revision 77671f03)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Winlogon
4  * FILE:            base/system/winlogon/winlogon.c
5  * PURPOSE:         Logon
6  * PROGRAMMERS:     Thomas Weidenmueller (w3seek@users.sourceforge.net)
7  *                  Filip Navara
8  *                  Herv� Poussineau (hpoussin@reactos.org)
9  */
10 
11 /* INCLUDES *****************************************************************/
12 
13 #include "winlogon.h"
14 
15 #include <ndk/cmfuncs.h>
16 
17 /* GLOBALS ******************************************************************/
18 
19 HINSTANCE hAppInstance;
20 PWLSESSION WLSession = NULL;
21 
22 /* FUNCTIONS *****************************************************************/
23 
24 static
25 BOOL
StartServicesManager(VOID)26 StartServicesManager(VOID)
27 {
28     STARTUPINFOW StartupInfo;
29     PROCESS_INFORMATION ProcessInformation;
30     LPCWSTR ServiceString = L"services.exe";
31     BOOL res;
32 
33     /* Start the service control manager (services.exe) */
34     ZeroMemory(&StartupInfo, sizeof(STARTUPINFOW));
35     StartupInfo.cb = sizeof(StartupInfo);
36     StartupInfo.lpReserved = NULL;
37     StartupInfo.lpDesktop = NULL;
38     StartupInfo.lpTitle = NULL;
39     StartupInfo.dwFlags = 0;
40     StartupInfo.cbReserved2 = 0;
41     StartupInfo.lpReserved2 = 0;
42 
43     TRACE("WL: Creating new process - %S\n", ServiceString);
44 
45     res = CreateProcessW(ServiceString,
46                          NULL,
47                          NULL,
48                          NULL,
49                          FALSE,
50                          DETACHED_PROCESS,
51                          NULL,
52                          NULL,
53                          &StartupInfo,
54                          &ProcessInformation);
55     if (!res)
56     {
57         ERR("WL: Failed to execute services (error %lu)\n", GetLastError());
58         return FALSE;
59     }
60 
61     TRACE("WL: Created new process - %S\n", ServiceString);
62 
63     CloseHandle(ProcessInformation.hThread);
64     CloseHandle(ProcessInformation.hProcess);
65 
66     TRACE("WL: StartServicesManager() done.\n");
67 
68     return TRUE;
69 }
70 
71 
72 static
73 BOOL
StartLsass(VOID)74 StartLsass(VOID)
75 {
76     STARTUPINFOW StartupInfo;
77     PROCESS_INFORMATION ProcessInformation;
78     LPCWSTR ServiceString = L"lsass.exe";
79     BOOL res;
80 
81     /* Start the local security authority subsystem (lsass.exe) */
82     ZeroMemory(&StartupInfo, sizeof(STARTUPINFOW));
83     StartupInfo.cb = sizeof(StartupInfo);
84     StartupInfo.lpReserved = NULL;
85     StartupInfo.lpDesktop = NULL;
86     StartupInfo.lpTitle = NULL;
87     StartupInfo.dwFlags = 0;
88     StartupInfo.cbReserved2 = 0;
89     StartupInfo.lpReserved2 = 0;
90 
91     TRACE("WL: Creating new process - %S\n", ServiceString);
92 
93     res = CreateProcessW(ServiceString,
94                          NULL,
95                          NULL,
96                          NULL,
97                          FALSE,
98                          DETACHED_PROCESS,
99                          NULL,
100                          NULL,
101                          &StartupInfo,
102                          &ProcessInformation);
103 
104     TRACE("WL: Created new process - %S\n", ServiceString);
105 
106     CloseHandle(ProcessInformation.hThread);
107     CloseHandle(ProcessInformation.hProcess);
108 
109     return res;
110 }
111 
112 
113 static
114 VOID
WaitForLsass(VOID)115 WaitForLsass(VOID)
116 {
117     HANDLE hEvent;
118     DWORD dwError;
119 
120     hEvent = CreateEventW(NULL,
121                           TRUE,
122                           FALSE,
123                           L"LSA_RPC_SERVER_ACTIVE");
124     if (hEvent == NULL)
125     {
126         dwError = GetLastError();
127         TRACE("WL: Failed to create the notification event (Error %lu)\n", dwError);
128 
129         if (dwError == ERROR_ALREADY_EXISTS)
130         {
131             hEvent = OpenEventW(SYNCHRONIZE,
132                                 FALSE,
133                                 L"LSA_RPC_SERVER_ACTIVE");
134             if (hEvent == NULL)
135             {
136                ERR("WL: Could not open the notification event (Error %lu)\n", GetLastError());
137                return;
138             }
139         }
140     }
141 
142     TRACE("WL: Wait for the LSA server!\n");
143     WaitForSingleObject(hEvent, INFINITE);
144     TRACE("WL: LSA server running!\n");
145 
146     CloseHandle(hEvent);
147 }
148 
149 
150 static
151 VOID
UpdateTcpIpInformation(VOID)152 UpdateTcpIpInformation(VOID)
153 {
154     LONG lError;
155     HKEY hKey = NULL;
156     DWORD dwType, dwSize;
157     PWSTR pszBuffer;
158     WCHAR szBuffer[128] = L"";
159 
160     lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
161                            L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
162                            0,
163                            KEY_QUERY_VALUE | KEY_SET_VALUE,
164                            &hKey);
165     if (lError != ERROR_SUCCESS)
166     {
167         ERR("WL: RegOpenKeyExW(\"HKLM\\System\\CurrentControlSet\\Services\\Tcpip\\Parameters\") failed (error %lu)\n", lError);
168         return;
169     }
170 
171     /*
172      * Read the "NV Hostname" value and copy it into the "Hostname" value.
173      */
174 
175     pszBuffer = szBuffer;
176     dwSize = ARRAYSIZE(szBuffer);
177 
178     lError = RegQueryValueExW(hKey,
179                               L"NV Hostname",
180                               NULL,
181                               &dwType,
182                               (LPBYTE)pszBuffer,
183                               &dwSize);
184     if (((lError == ERROR_INSUFFICIENT_BUFFER) || (lError == ERROR_MORE_DATA)) && (dwType == REG_SZ))
185     {
186         pszBuffer = HeapAlloc(GetProcessHeap(), 0, dwSize);
187         if (pszBuffer)
188         {
189             lError = RegQueryValueExW(hKey,
190                                       L"NV Hostname",
191                                       NULL,
192                                       &dwType,
193                                       (LPBYTE)pszBuffer,
194                                       &dwSize);
195         }
196         else
197         {
198             ERR("WL: Could not reallocate memory for pszBuffer\n");
199         }
200     }
201     if ((lError == ERROR_SUCCESS) && (dwType == REG_SZ))
202     {
203         TRACE("NV Hostname is '%S'.\n", pszBuffer);
204 
205         lError = RegSetValueExW(hKey,
206                                 L"Hostname",
207                                 0,
208                                 REG_SZ,
209                                 (LPBYTE)pszBuffer,
210                                 dwSize);
211         if (lError != ERROR_SUCCESS)
212             ERR("WL: RegSetValueExW(\"Hostname\") failed (error %lu)\n", lError);
213     }
214 
215     /*
216      * Read the "NV Domain" value and copy it into the "Domain" value.
217      */
218 
219     // pszBuffer = szBuffer;
220     // dwSize = ARRAYSIZE(szBuffer);
221 
222     lError = RegQueryValueExW(hKey,
223                               L"NV Domain",
224                               NULL,
225                               &dwType,
226                               (LPBYTE)pszBuffer,
227                               &dwSize);
228     if (((lError == ERROR_INSUFFICIENT_BUFFER) || (lError == ERROR_MORE_DATA)) && (dwType == REG_SZ))
229     {
230         if (pszBuffer != szBuffer)
231         {
232             PWSTR pszNewBuffer;
233             pszNewBuffer = HeapReAlloc(GetProcessHeap(), 0, pszBuffer, dwSize);
234             if (pszNewBuffer)
235             {
236                 pszBuffer = pszNewBuffer;
237             }
238             else
239             {
240                 HeapFree(GetProcessHeap(), 0, pszBuffer);
241                 pszBuffer = NULL;
242             }
243         }
244         else
245         {
246             pszBuffer = HeapAlloc(GetProcessHeap(), 0, dwSize);
247         }
248         if (pszBuffer)
249         {
250             lError = RegQueryValueExW(hKey,
251                                       L"NV Domain",
252                                       NULL,
253                                       &dwType,
254                                       (LPBYTE)pszBuffer,
255                                       &dwSize);
256         }
257         else
258         {
259             ERR("WL: Could not reallocate memory for pszBuffer\n");
260         }
261     }
262     if ((lError == ERROR_SUCCESS) && (dwType == REG_SZ))
263     {
264         TRACE("NV Domain is '%S'.\n", pszBuffer);
265 
266         lError = RegSetValueExW(hKey,
267                                 L"Domain",
268                                 0,
269                                 REG_SZ,
270                                 (LPBYTE)pszBuffer,
271                                 dwSize);
272         if (lError != ERROR_SUCCESS)
273             ERR("WL: RegSetValueExW(\"Domain\") failed (error %lu)\n", lError);
274     }
275 
276     if (pszBuffer != szBuffer)
277         HeapFree(GetProcessHeap(), 0, pszBuffer);
278 
279     RegCloseKey(hKey);
280 }
281 
282 
283 static
284 BOOL
InitKeyboardLayouts(VOID)285 InitKeyboardLayouts(VOID)
286 {
287     WCHAR wszKeyName[12], wszKLID[10];
288     DWORD dwSize = sizeof(wszKLID), dwType, i = 1;
289     HKEY hKey;
290     UINT Flags;
291     BOOL bRet = FALSE;
292 
293     /* Open registry key with preloaded layouts */
294     if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Keyboard Layout\\Preload", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
295     {
296         while (TRUE)
297         {
298             /* Read values with integer names only */
299             swprintf(wszKeyName, L"%d", i++);
300             if (RegQueryValueExW(hKey, wszKeyName, NULL, &dwType, (LPBYTE)wszKLID, &dwSize) != ERROR_SUCCESS)
301             {
302                 /* There is no more entries */
303                 break;
304             }
305 
306             /* Only REG_SZ values are valid */
307             if (dwType != REG_SZ)
308             {
309                 ERR("Wrong type: %ws!\n", wszKLID);
310                 continue;
311             }
312 
313             /* Load keyboard layout with given locale id */
314             Flags = KLF_SUBSTITUTE_OK;
315             if (i > 1)
316                 Flags |= KLF_NOTELLSHELL|KLF_REPLACELANG;
317             else // First layout
318                 Flags |= KLF_ACTIVATE; // |0x40000000
319             if (!LoadKeyboardLayoutW(wszKLID, Flags))
320             {
321                 ERR("LoadKeyboardLayoutW(%ws) failed!\n", wszKLID);
322                 continue;
323             }
324             else
325             {
326                 /* We loaded at least one layout - success */
327                 bRet = TRUE;
328             }
329         }
330 
331         /* Close the key now */
332         RegCloseKey(hKey);
333     }
334     else
335         WARN("RegOpenKeyExW(Keyboard Layout\\Preload) failed!\n");
336 
337     if (!bRet)
338     {
339         /* If we failed, load US keyboard layout */
340         if (LoadKeyboardLayoutW(L"00000409", KLF_ACTIVATE | KLF_SUBSTITUTE_OK | KLF_REPLACELANG | KLF_SETFORPROCESS))
341             bRet = TRUE;
342     }
343 
344     return bRet;
345 }
346 
347 
348 BOOL
DisplayStatusMessage(IN PWLSESSION Session,IN HDESK hDesktop,IN UINT ResourceId)349 DisplayStatusMessage(
350      IN PWLSESSION Session,
351      IN HDESK hDesktop,
352      IN UINT ResourceId)
353 {
354     WCHAR StatusMsg[MAX_PATH];
355 
356     if (Session->Gina.Version < WLX_VERSION_1_3)
357         return TRUE;
358 
359     if (Session->SuppressStatus)
360         return TRUE;
361 
362     if (LoadStringW(hAppInstance, ResourceId, StatusMsg, MAX_PATH) == 0)
363         return FALSE;
364 
365     return Session->Gina.Functions.WlxDisplayStatusMessage(Session->Gina.Context, hDesktop, 0, NULL, StatusMsg);
366 }
367 
368 
369 BOOL
RemoveStatusMessage(IN PWLSESSION Session)370 RemoveStatusMessage(
371     IN PWLSESSION Session)
372 {
373     if (Session->Gina.Version < WLX_VERSION_1_3)
374         return TRUE;
375 
376     return Session->Gina.Functions.WlxRemoveStatusMessage(Session->Gina.Context);
377 }
378 
379 
380 static
381 INT_PTR
382 CALLBACK
GinaLoadFailedWindowProc(IN HWND hwndDlg,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam)383 GinaLoadFailedWindowProc(
384     IN HWND hwndDlg,
385     IN UINT uMsg,
386     IN WPARAM wParam,
387     IN LPARAM lParam)
388 {
389     switch (uMsg)
390     {
391         case WM_COMMAND:
392         {
393             switch (LOWORD(wParam))
394             {
395                 case IDOK:
396                     EndDialog(hwndDlg, IDOK);
397                     return TRUE;
398             }
399             break;
400         }
401 
402         case WM_INITDIALOG:
403         {
404             int len;
405             WCHAR templateText[MAX_PATH], text[MAX_PATH];
406 
407             len = GetDlgItemTextW(hwndDlg, IDC_GINALOADFAILED, templateText, MAX_PATH);
408             if (len)
409             {
410                 wsprintfW(text, templateText, (LPWSTR)lParam);
411                 SetDlgItemTextW(hwndDlg, IDC_GINALOADFAILED, text);
412             }
413 
414             SetFocus(GetDlgItem(hwndDlg, IDOK));
415             return TRUE;
416         }
417 
418         case WM_CLOSE:
419         {
420             EndDialog(hwndDlg, IDCANCEL);
421             return TRUE;
422         }
423     }
424 
425     return FALSE;
426 }
427 
428 
429 int
430 WINAPI
WinMain(IN HINSTANCE hInstance,IN HINSTANCE hPrevInstance,IN LPSTR lpCmdLine,IN int nShowCmd)431 WinMain(
432     IN HINSTANCE hInstance,
433     IN HINSTANCE hPrevInstance,
434     IN LPSTR lpCmdLine,
435     IN int nShowCmd)
436 {
437 #if 0
438     LSA_STRING ProcessName, PackageName;
439     HANDLE LsaHandle;
440     LSA_OPERATIONAL_MODE Mode;
441     BOOLEAN Old;
442     ULONG AuthenticationPackage;
443     NTSTATUS Status;
444 #endif
445     ULONG HardErrorResponse;
446     MSG Msg;
447 
448     UNREFERENCED_PARAMETER(hPrevInstance);
449     UNREFERENCED_PARAMETER(lpCmdLine);
450     UNREFERENCED_PARAMETER(nShowCmd);
451 
452     hAppInstance = hInstance;
453 
454     /* Make us critical */
455     RtlSetProcessIsCritical(TRUE, NULL, FALSE);
456     RtlSetThreadIsCritical(TRUE, NULL, FALSE);
457 
458     /* Update the cached TCP/IP Information in the registry */
459     UpdateTcpIpInformation();
460 
461     if (!RegisterLogonProcess(GetCurrentProcessId(), TRUE))
462     {
463         ERR("WL: Could not register logon process\n");
464         NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, NULL, OptionOk, &HardErrorResponse);
465         ExitProcess(1);
466     }
467 
468     WLSession = (PWLSESSION)HeapAlloc(GetProcessHeap(), 0, sizeof(WLSESSION));
469     if (!WLSession)
470     {
471         ERR("WL: Could not allocate memory for winlogon instance\n");
472         NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, NULL, OptionOk, &HardErrorResponse);
473         ExitProcess(1);
474     }
475 
476     ZeroMemory(WLSession, sizeof(WLSESSION));
477     WLSession->DialogTimeout = 120; /* 2 minutes */
478 
479     /* Initialize the dialog tracking list */
480     InitDialogListHead();
481 
482     if (!CreateWindowStationAndDesktops(WLSession))
483     {
484         ERR("WL: Could not create window station and desktops\n");
485         NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, NULL, OptionOk, &HardErrorResponse);
486         ExitProcess(1);
487     }
488 
489     LockWorkstation(WLSession);
490 
491     /* Load default keyboard layouts */
492     if (!InitKeyboardLayouts())
493     {
494         ERR("WL: Could not preload keyboard layouts\n");
495         NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, NULL, OptionOk, &HardErrorResponse);
496         ExitProcess(1);
497     }
498 
499     if (!StartRpcServer())
500     {
501         ERR("WL: Could not start the RPC server\n");
502         NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, NULL, OptionOk, &HardErrorResponse);
503         ExitProcess(1);
504     }
505 
506     if (!StartServicesManager())
507     {
508         ERR("WL: Could not start services.exe\n");
509         NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, NULL, OptionOk, &HardErrorResponse);
510         ExitProcess(1);
511     }
512 
513     if (!StartLsass())
514     {
515         ERR("WL: Failed to start lsass.exe service (error %lu)\n", GetLastError());
516         NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED, 0, 0, NULL, OptionOk, &HardErrorResponse);
517         ExitProcess(1);
518     }
519 
520     /* Wait for the LSA server */
521     WaitForLsass();
522 
523     /* Init Notifications */
524     InitNotifications();
525 
526     /* Load and initialize gina */
527     if (!GinaInit(WLSession))
528     {
529         ERR("WL: Failed to initialize Gina\n");
530         // FIXME: Retrieve the real name of the GINA DLL we were trying to load.
531         // It is known only inside the GinaInit function...
532         DialogBoxParam(hAppInstance, MAKEINTRESOURCE(IDD_GINALOADFAILED), GetDesktopWindow(), GinaLoadFailedWindowProc, (LPARAM)L"msgina.dll");
533         HandleShutdown(WLSession, WLX_SAS_ACTION_SHUTDOWN_REBOOT);
534         ExitProcess(1);
535     }
536 
537     DisplayStatusMessage(WLSession, WLSession->WinlogonDesktop, IDS_REACTOSISSTARTINGUP);
538 
539 #if 0
540     /* Connect to NetLogon service (lsass.exe) */
541     /* Real winlogon uses "Winlogon" */
542     RtlInitUnicodeString((PUNICODE_STRING)&ProcessName, L"Winlogon");
543     Status = LsaRegisterLogonProcess(&ProcessName, &LsaHandle, &Mode);
544     if (Status == STATUS_PORT_CONNECTION_REFUSED)
545     {
546         /* Add the 'SeTcbPrivilege' privilege and try again */
547         Status = RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, TRUE, &Old);
548         if (!NT_SUCCESS(Status))
549         {
550             ERR("RtlAdjustPrivilege() failed with error %lu\n", LsaNtStatusToWinError(Status));
551             return 1;
552         }
553 
554         Status = LsaRegisterLogonProcess(&ProcessName, &LsaHandle, &Mode);
555     }
556 
557     if (!NT_SUCCESS(Status))
558     {
559         ERR("LsaRegisterLogonProcess() failed with error %lu\n", LsaNtStatusToWinError(Status));
560         return 1;
561     }
562 
563     RtlInitUnicodeString((PUNICODE_STRING)&PackageName, MICROSOFT_KERBEROS_NAME_W);
564     Status = LsaLookupAuthenticationPackage(LsaHandle, &PackageName, &AuthenticationPackage);
565     if (!NT_SUCCESS(Status))
566     {
567         ERR("LsaLookupAuthenticationPackage() failed with error %lu\n", LsaNtStatusToWinError(Status));
568         LsaDeregisterLogonProcess(LsaHandle);
569         return 1;
570     }
571 #endif
572 
573     CallNotificationDlls(WLSession, StartupHandler);
574 
575     /* Create a hidden window to get SAS notifications */
576     if (!InitializeSAS(WLSession))
577     {
578         ERR("WL: Failed to initialize SAS\n");
579         ExitProcess(2);
580     }
581 
582     // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_PREPARENETWORKCONNECTIONS);
583     // DisplayStatusMessage(Session, Session->WinlogonDesktop, IDS_APPLYINGCOMPUTERSETTINGS);
584 
585     /* Display logged out screen */
586     WLSession->LogonState = STATE_INIT;
587     RemoveStatusMessage(WLSession);
588 
589     /* Check for pending setup */
590     if (GetSetupType() != 0)
591     {
592         /* Run setup and reboot when done */
593         TRACE("WL: Setup mode detected\n");
594         RunSetup();
595     }
596     else
597     {
598         PostMessageW(WLSession->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_CTRL_ALT_DEL, 0);
599     }
600 
601     (void)LoadLibraryW(L"sfc_os.dll");
602 
603     /* Tell kernel that CurrentControlSet is good (needed
604      * to support Last good known configuration boot) */
605     NtInitializeRegistry(CM_BOOT_FLAG_ACCEPTED | 1);
606 
607     /* Message loop for the SAS window */
608     while (GetMessageW(&Msg, WLSession->SASWindow, 0, 0))
609     {
610         TranslateMessage(&Msg);
611         DispatchMessageW(&Msg);
612     }
613 
614     CleanupNotifications();
615 
616     /* We never go there */
617 
618     return 0;
619 }
620