VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxGINA/Dialog.cpp@ 93115

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.1 KB
Line 
1/* $Id: Dialog.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VBoxGINA - Windows Logon DLL for VirtualBox, Dialog Code.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/win/windows.h>
19#include <stdio.h> /* Needed for swprintf() */
20
21#include <VBox/VBoxGuestLib.h>
22#include <iprt/errcore.h>
23
24#include "Dialog.h"
25#include "WinWlx.h"
26#include "Helper.h"
27#include "VBoxGINA.h"
28
29
30/*
31 * Dialog IDs for legacy Windows OSes (e.g. NT 4.0).
32 */
33#define IDD_WLXDIAPLAYSASNOTICE_DIALOG 1400
34#define IDD_WLXLOGGEDOUTSAS_DIALOG 1450
35/** Change password dialog: To change the current
36 * account password. */
37#define IDD_CHANGE_PASSWORD_DIALOG 1550
38#define IDD_WLXLOGGEDONSAS_DIALOG 1650
39/** Security dialog: To lock the workstation, log off
40 * change password, ... */
41#define IDD_SECURITY_DIALOG 1800
42/** Locked dialog: To unlock the currently lockted
43 * workstation. */
44#define IDD_WLXWKSTALOCKEDSAS_DIALOG 1850
45/** Shutdown dialog: To either restart, logoff current
46 * user or shutdown the workstation. */
47#define IDD_SHUTDOWN_DIALOG 2200
48/** Logoff dialog: "Do you really want to logoff?". */
49#define IDD_LOGOFF_DIALOG 2250
50
51
52/*
53 * Dialog IDs for Windows 2000 and up.
54 */
55#define IDD_WLXLOGGEDOUTSAS_DIALOG2 1500
56/** Change password dialog: To change the current
57 * account password. */
58#define IDD_CHANGE_PASSWORD_DIALOG2 1700
59/** Locked dialog: To unlock the currently lockted
60 * workstation. */
61#define IDD_WLXWKSTALOCKEDSAS_DIALOG2 1950
62
63
64/*
65 * Control IDs.
66 */
67#define IDC_WLXLOGGEDOUTSAS_USERNAME 1453
68#define IDC_WLXLOGGEDOUTSAS_USERNAME2 1502
69#define IDC_WLXLOGGEDOUTSAS_PASSWORD 1454
70#define IDC_WLXLOGGEDOUTSAS_PASSWORD2 1503
71#define IDC_WLXLOGGEDOUTSAS_DOMAIN 1455
72#define IDC_WLXLOGGEDOUTSAS_DOMAIN2 1504
73
74#define IDC_WKSTALOCKED_USERNAME 1953
75#define IDC_WKSTALOCKED_PASSWORD 1954
76#define IDC_WKSTALOCKEd_DOMAIN 1856
77#define IDC_WKSTALOCKED_DOMAIN2 1956
78
79
80/*
81 * Own IDs.
82 */
83#define IDT_BASE WM_USER + 1100 /* Timer ID base. */
84#define IDT_LOGGEDONDLG_POLL IDT_BASE + 1
85#define IDT_LOCKEDDLG_POLL IDT_BASE + 2
86
87static DLGPROC g_pfnWlxLoggedOutSASDlgProc = NULL;
88static DLGPROC g_pfnWlxLockedSASDlgProc = NULL;
89
90static PWLX_DIALOG_BOX_PARAM g_pfnWlxDialogBoxParam = NULL;
91
92int WINAPI MyWlxDialogBoxParam (HANDLE, HANDLE, LPWSTR, HWND, DLGPROC, LPARAM);
93
94void hookDialogBoxes(PVOID pWinlogonFunctions, DWORD dwWlxVersion)
95{
96 if (!pWinlogonFunctions) /* Needed for testcase. */
97 return;
98
99 VBoxGINAVerbose(0, "VBoxGINA::hookDialogBoxes\n");
100
101 /* this is version dependent */
102 switch (dwWlxVersion)
103 {
104 case WLX_VERSION_1_0:
105 {
106 g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_0)pWinlogonFunctions)->WlxDialogBoxParam;
107 ((PWLX_DISPATCH_VERSION_1_0)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam;
108 break;
109 }
110
111 case WLX_VERSION_1_1:
112 {
113 g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_1)pWinlogonFunctions)->WlxDialogBoxParam;
114 ((PWLX_DISPATCH_VERSION_1_1)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam;
115 break;
116 }
117
118 case WLX_VERSION_1_2:
119 {
120 g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_2)pWinlogonFunctions)->WlxDialogBoxParam;
121 ((PWLX_DISPATCH_VERSION_1_2)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam;
122 break;
123 }
124
125 case WLX_VERSION_1_3:
126 {
127 g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_3)pWinlogonFunctions)->WlxDialogBoxParam;
128 ((PWLX_DISPATCH_VERSION_1_3)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam;
129 break;
130 }
131
132 case WLX_VERSION_1_4:
133 {
134 g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_4)pWinlogonFunctions)->WlxDialogBoxParam;
135 ((PWLX_DISPATCH_VERSION_1_4)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam;
136 break;
137 }
138
139 default:
140 {
141 VBoxGINAVerbose(0, "VBoxGINA::hookDialogBoxes: unrecognized version '%d', nothing hooked!\n", dwWlxVersion);
142 /* not good, don't do anything */
143 break;
144 }
145 }
146}
147
148/**
149 * Enters credentials into the given text fields.
150 *
151 * @return IPRT status code.
152 * @param hwndDlg Handle of dialog to enter credentials into.
153 * @param hwndUserId Handle of username text field. Optional.
154 * @param hwndPassword Handle of password text field. Optional.
155 * @param hwndDomain Handle of domain text field. Optional.
156 * @param pwszUser Username to enter into username text field.
157 * @param pwszPassword Password to enter into password text field.
158 * @param pwszDomain Domain to enter into domain text field.
159 */
160int credentialsToUI(HWND hwndDlg,
161 HWND hwndUserId, HWND hwndPassword, HWND hwndDomain,
162 PCRTUTF16 pwszUser, PCRTUTF16 pwszPassword, PCRTUTF16 pwszDomain)
163{
164 RT_NOREF(hwndDlg);
165 BOOL bIsFQDN = FALSE;
166 wchar_t szUserFQDN[512]; /* VMMDEV_CREDENTIALS_STRLEN + 255 bytes max. for FQDN */
167 if (hwndDomain)
168 {
169 /* search the domain combo box for our required domain and select it */
170 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Trying to find domain entry in combo box ...\n");
171 DWORD dwIndex = (DWORD) SendMessage(hwndDomain, CB_FINDSTRING,
172 0, (LPARAM)pwszDomain);
173 if (dwIndex != CB_ERR)
174 {
175 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Found domain at combo box pos %ld\n", dwIndex);
176 SendMessage(hwndDomain, CB_SETCURSEL, (WPARAM) dwIndex, 0);
177 EnableWindow(hwndDomain, FALSE);
178 }
179 else
180 {
181 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain not found in combo box ...\n");
182
183 /* If the domain value has a dot (.) in it, it is a FQDN (Fully Qualified Domain Name)
184 * which will not work with the combo box selection because Windows only keeps the
185 * NETBIOS names to the left most part of the domain name there. Of course a FQDN
186 * then will not be found by the search in the block above.
187 *
188 * To solve this problem the FQDN domain value will be appended at the user name value
189 * (Kerberos style) using an "@", e.g. "<user-name>@full.qualified.domain".
190 *
191 */
192 size_t l = wcslen(pwszDomain);
193 if (l > 255)
194 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Warning! FQDN (domain) is too long (max 255 bytes), will be truncated!\n");
195
196 if (wcslen(pwszUser) > 0) /* We need a user name that we can use in caes of a FQDN */
197 {
198 if (l > 16) /* Domain name is longer than 16 chars, cannot be a NetBIOS name anymore */
199 {
200 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain seems to be a FQDN (length)!\n");
201 bIsFQDN = TRUE;
202 }
203 else if ( l > 0
204 && wcsstr(pwszDomain, L".") != NULL) /* if we found a dot (.) in the domain name, this has to be a FQDN */
205 {
206 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain seems to be a FQDN (dot)!\n");
207 bIsFQDN = TRUE;
208 }
209
210 if (bIsFQDN)
211 {
212 swprintf(szUserFQDN, sizeof(szUserFQDN) / sizeof(wchar_t), L"%s@%s", pwszUser, pwszDomain);
213 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: FQDN user name is now: %s!\n", szUserFQDN);
214 }
215 }
216 }
217 }
218 if (hwndUserId)
219 {
220 if (!bIsFQDN)
221 SendMessage(hwndUserId, WM_SETTEXT, 0, (LPARAM)pwszUser);
222 else
223 SendMessage(hwndUserId, WM_SETTEXT, 0, (LPARAM)szUserFQDN);
224 }
225 if (hwndPassword)
226 SendMessage(hwndPassword, WM_SETTEXT, 0, (LPARAM)pwszPassword);
227
228 return VINF_SUCCESS; /** @todo */
229}
230
231/**
232 * Tries to retrieve credentials and enters them into the specified windows,
233 * optionally followed by a button press to confirm/abort the dialog.
234 *
235 * @return IPRT status code.
236 * @param hwndDlg Handle of dialog to enter credentials into.
237 * @param hwndUserId Handle of username text field. Optional.
238 * @param hwndPassword Handle of password text field. Optional.
239 * @param hwndDomain Handle of domain text field. Optional.
240 * @param wButtonToPress Button ID of dialog to press after successful
241 * retrieval + storage. If set to 0 no button will
242 * be pressed.
243 */
244int credentialsHandle(HWND hwndDlg,
245 HWND hwndUserId, HWND hwndPassword, HWND hwndDomain,
246 WORD wButtonToPress)
247{
248 int rc = VINF_SUCCESS;
249
250 if (!VBoxGINAHandleCurrentSession())
251 rc = VERR_NOT_FOUND;
252
253 if (RT_SUCCESS(rc))
254 {
255 rc = VbglR3CredentialsQueryAvailability();
256 if (RT_FAILURE(rc))
257 {
258 if (rc != VERR_NOT_FOUND)
259 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: error querying for credentials, rc=%Rrc\n", rc);
260 }
261 }
262
263 if (RT_SUCCESS(rc))
264 {
265 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: credentials available\n");
266
267 /*
268 * Set status to "terminating" to let the host know this module now
269 * tries to receive and use passed credentials so that credentials from
270 * the host won't be sent twice.
271 */
272 VBoxGINAReportStatus(VBoxGuestFacilityStatus_Terminating);
273
274 PRTUTF16 pwszUser, pwszPassword, pwszDomain;
275 rc = VbglR3CredentialsRetrieveUtf16(&pwszUser, &pwszPassword, &pwszDomain);
276 if (RT_SUCCESS(rc))
277 {
278#ifdef DEBUG
279 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: retrieved credentials: user=%ls, password=%ls, domain=%ls\n",
280 pwszUser, pwszPassword, pwszDomain);
281#else
282 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: retrieved credentials: user=%ls, password=XXX, domain=%ls\n",
283 pwszUser, pwszDomain);
284#endif
285 /* Fill in credentials to appropriate UI elements. */
286 rc = credentialsToUI(hwndDlg,
287 hwndUserId, hwndPassword, hwndDomain,
288 pwszUser, pwszPassword, pwszDomain);
289 if (RT_SUCCESS(rc))
290 {
291 /* Confirm/cancel the dialog by pressing the appropriate button. */
292 if (wButtonToPress)
293 {
294 WPARAM wParam = MAKEWPARAM(wButtonToPress, BN_CLICKED);
295 PostMessage(hwndDlg, WM_COMMAND, wParam, 0);
296 }
297 }
298
299 VbglR3CredentialsDestroyUtf16(pwszUser, pwszPassword, pwszDomain,
300 3 /* Passes */);
301 }
302 }
303
304#ifdef DEBUG
305 VBoxGINAVerbose(3, "VBoxGINA::credentialsHandle: returned with rc=%Rrc\n", rc);
306#endif
307 return rc;
308}
309
310INT_PTR CALLBACK MyWlxLoggedOutSASDlgProc(HWND hwndDlg, // handle to dialog box
311 UINT uMsg, // message
312 WPARAM wParam, // first message parameter
313 LPARAM lParam) // second message parameter
314{
315 BOOL bResult;
316 static HWND s_hwndUserId, s_hwndPassword, s_hwndDomain = 0;
317
318 /*VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc\n");*/
319
320 //
321 // Pass on to MSGINA first.
322 //
323 bResult = g_pfnWlxLoggedOutSASDlgProc(hwndDlg, uMsg, wParam, lParam);
324
325 //
326 // We are only interested in the WM_INITDIALOG message.
327 //
328 switch (uMsg)
329 {
330 case WM_INITDIALOG:
331 {
332 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: got WM_INITDIALOG\n");
333
334 /* get the entry fields */
335 s_hwndUserId = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_USERNAME);
336 if (!s_hwndUserId)
337 s_hwndUserId = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_USERNAME2);
338 s_hwndPassword = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_PASSWORD);
339 if (!s_hwndPassword)
340 s_hwndPassword = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_PASSWORD2);
341 s_hwndDomain = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_DOMAIN);
342 if (!s_hwndDomain)
343 s_hwndDomain = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_DOMAIN2);
344
345 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: hwndUserId: %x, hwndPassword: %d, hwndDomain: %d\n",
346 s_hwndUserId, s_hwndPassword, s_hwndDomain);
347
348 /* terminate the credentials poller thread, it's done is job */
349 VBoxGINACredentialsPollerTerminate();
350
351 int rc = credentialsHandle(hwndDlg,
352 s_hwndUserId, s_hwndPassword, s_hwndDomain,
353 IDOK /* Button */);
354 if (RT_FAILURE(rc))
355 {
356 /*
357 * The dialog is there but we don't have any credentials.
358 * Create a timer and poll for them.
359 */
360 UINT_PTR uTimer = SetTimer(hwndDlg, IDT_LOGGEDONDLG_POLL, 200, NULL);
361 if (!uTimer)
362 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: failed creating timer! Last error: %ld\n",
363 GetLastError());
364 }
365 break;
366 }
367
368 case WM_TIMER:
369 {
370 /* is it our credentials poller timer? */
371 if (wParam == IDT_LOGGEDONDLG_POLL)
372 {
373 int rc = credentialsHandle(hwndDlg,
374 s_hwndUserId, s_hwndPassword, s_hwndDomain,
375 IDOK /* Button */);
376 if (RT_SUCCESS(rc))
377 {
378 /* we don't need the timer any longer */
379 KillTimer(hwndDlg, IDT_LOGGEDONDLG_POLL);
380 }
381 }
382 break;
383 }
384
385 case WM_DESTROY:
386 KillTimer(hwndDlg, IDT_LOGGEDONDLG_POLL);
387 break;
388 }
389 return bResult;
390}
391
392
393INT_PTR CALLBACK MyWlxLockedSASDlgProc(HWND hwndDlg, // handle to dialog box
394 UINT uMsg, // message
395 WPARAM wParam, // first message parameter
396 LPARAM lParam) // second message parameter
397{
398 BOOL bResult;
399 static HWND s_hwndPassword = 0;
400
401 /*VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc\n");*/
402
403 //
404 // Pass on to MSGINA first.
405 //
406 bResult = g_pfnWlxLockedSASDlgProc(hwndDlg, uMsg, wParam, lParam);
407
408 //
409 // We are only interested in the WM_INITDIALOG message.
410 //
411 switch (uMsg)
412 {
413 case WM_INITDIALOG:
414 {
415 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: WM_INITDIALOG\n");
416
417 /* get the entry fields */
418 s_hwndPassword = GetDlgItem(hwndDlg, IDC_WKSTALOCKED_PASSWORD);
419 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: hwndPassword: %d\n", s_hwndPassword);
420
421 /* terminate the credentials poller thread, it's done is job */
422 VBoxGINACredentialsPollerTerminate();
423
424 int rc = credentialsHandle(hwndDlg,
425 NULL /* Username */, s_hwndPassword, NULL /* Domain */,
426 IDOK /* Button */);
427 if (RT_FAILURE(rc))
428 {
429 /*
430 * The dialog is there but we don't have any credentials.
431 * Create a timer and poll for them.
432 */
433 UINT_PTR uTimer = SetTimer(hwndDlg, IDT_LOCKEDDLG_POLL, 200, NULL);
434 if (!uTimer)
435 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: failed creating timer! Last error: %ld\n",
436 GetLastError());
437 }
438 break;
439 }
440
441 case WM_TIMER:
442 {
443 /* is it our credentials poller timer? */
444 if (wParam == IDT_LOCKEDDLG_POLL)
445 {
446 int rc = credentialsHandle(hwndDlg,
447 NULL /* Username */, s_hwndPassword, NULL /* Domain */,
448 IDOK /* Button */);
449 if (RT_SUCCESS(rc))
450 {
451 /* we don't need the timer any longer */
452 KillTimer(hwndDlg, IDT_LOCKEDDLG_POLL);
453 }
454 }
455 break;
456 }
457
458 case WM_DESTROY:
459 {
460 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: WM_DESTROY\n");
461
462 /* Because this is the only point where we know within our module that the locked
463 * dialog has been closed by a valid unlock password we have to set the appropriate
464 * facility status here. */
465 VBoxGINAReportStatus(VBoxGuestFacilityStatus_Terminated);
466
467 KillTimer(hwndDlg, IDT_LOCKEDDLG_POLL);
468 break;
469 }
470 }
471 return bResult;
472}
473
474
475int WINAPI MyWlxDialogBoxParam(HANDLE hWlx,
476 HANDLE hInst,
477 LPWSTR pszTemplate,
478 HWND hwndOwner,
479 DLGPROC dlgprc,
480 LPARAM dwInitParam)
481{
482 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: pszTemplate=%ls\n", pszTemplate);
483
484 VBoxGINAReportStatus(VBoxGuestFacilityStatus_Active);
485
486 //
487 // We only know MSGINA dialogs by identifiers.
488 //
489 if (((uintptr_t)pszTemplate >> 16) == 0)
490 {
491 //
492 // Hook appropriate dialog boxes as necessary.
493 //
494 switch ((DWORD)(uintptr_t)pszTemplate)
495 {
496 case IDD_WLXDIAPLAYSASNOTICE_DIALOG:
497 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: SAS notice dialog displayed; not handled\n");
498 break;
499
500 case IDD_WLXLOGGEDOUTSAS_DIALOG: /* Windows NT 4.0. */
501 case IDD_WLXLOGGEDOUTSAS_DIALOG2: /* Windows 2000 and up. */
502 {
503 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: returning hooked SAS logged out dialog\n");
504 g_pfnWlxLoggedOutSASDlgProc = dlgprc;
505 return g_pfnWlxDialogBoxParam(hWlx, hInst, pszTemplate, hwndOwner,
506 MyWlxLoggedOutSASDlgProc, dwInitParam);
507 }
508
509 case IDD_SECURITY_DIALOG:
510 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: Security dialog displayed; not handled\n");
511 break;
512
513 case IDD_WLXWKSTALOCKEDSAS_DIALOG: /* Windows NT 4.0. */
514 case IDD_WLXWKSTALOCKEDSAS_DIALOG2: /* Windows 2000 and up. */
515 {
516 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: returning hooked SAS locked dialog\n");
517 g_pfnWlxLockedSASDlgProc = dlgprc;
518 return g_pfnWlxDialogBoxParam(hWlx, hInst, pszTemplate, hwndOwner,
519 MyWlxLockedSASDlgProc, dwInitParam);
520 }
521
522 /** @todo Add other hooking stuff here. */
523
524 default:
525 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: dialog %p (%u) not handled\n",
526 pszTemplate, (DWORD)(uintptr_t)pszTemplate);
527 break;
528 }
529 }
530
531 /* The rest will be redirected. */
532 return g_pfnWlxDialogBoxParam(hWlx, hInst, pszTemplate, hwndOwner, dlgprc, dwInitParam);
533}
534
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette