xref: /reactos/base/system/winlogon/winlogon.c (revision e1338178)
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
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
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
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
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
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
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
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
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
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