VirtualBox

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

Last change on this file since 40388 was 40214, checked in by vboxsync, 13 years ago

VBoxGINA: Added support for auto-logon facility, added guest log support, misc. refactoring, added basic testcase.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.0 KB
Line 
1/* $Id: Dialog.cpp 40214 2012-02-22 14:36:09Z vboxsync $ */
2/** @file
3 * VBoxGINA - Windows Logon DLL for VirtualBox, Dialog Code.
4 */
5
6/*
7 *
8 * Copyright (C) 2006-2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <windows.h>
20#include <stdio.h> /* Needed for swprintf() */
21
22#include <VBox/VboxGuestLib.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 BOOL bIsFQDN = FALSE;
165 wchar_t szUserFQDN[512]; /* VMMDEV_CREDENTIALS_STRLEN + 255 bytes max. for FQDN */
166 if (hwndDomain)
167 {
168 /* search the domain combo box for our required domain and select it */
169 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Trying to find domain entry in combo box ...\n");
170 DWORD dwIndex = (DWORD) SendMessage(hwndDomain, CB_FINDSTRING,
171 0, (LPARAM)pwszDomain);
172 if (dwIndex != CB_ERR)
173 {
174 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Found domain at combo box pos %ld\n", dwIndex);
175 SendMessage(hwndDomain, CB_SETCURSEL, (WPARAM) dwIndex, 0);
176 EnableWindow(hwndDomain, FALSE);
177 }
178 else
179 {
180 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain not found in combo box ...\n");
181
182 /* If the domain value has a dot (.) in it, it is a FQDN (Fully Qualified Domain Name)
183 * which will not work with the combo box selection because Windows only keeps the
184 * NETBIOS names to the left most part of the domain name there. Of course a FQDN
185 * then will not be found by the search in the block above.
186 *
187 * To solve this problem the FQDN domain value will be appended at the user name value
188 * (Kerberos style) using an "@", e.g. "<user-name>@full.qualified.domain".
189 *
190 */
191 size_t l = wcslen(pwszDomain);
192 if (l > 255)
193 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Warning! FQDN (domain) is too long (max 255 bytes), will be truncated!\n");
194
195 if (wcslen(pwszUser) > 0) /* We need a user name that we can use in caes of a FQDN */
196 {
197 if (l > 16) /* Domain name is longer than 16 chars, cannot be a NetBIOS name anymore */
198 {
199 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain seems to be a FQDN (length)!\n");
200 bIsFQDN = TRUE;
201 }
202 else if ( l > 0
203 && wcsstr(pwszDomain, L".") != NULL) /* if we found a dot (.) in the domain name, this has to be a FQDN */
204 {
205 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain seems to be a FQDN (dot)!\n");
206 bIsFQDN = TRUE;
207 }
208
209 if (bIsFQDN)
210 {
211 swprintf(szUserFQDN, sizeof(szUserFQDN) / sizeof(wchar_t), L"%s@%s", pwszUser, pwszDomain);
212 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: FQDN user name is now: %s!\n", szUserFQDN);
213 }
214 }
215 }
216 }
217 if (hwndUserId)
218 {
219 if (!bIsFQDN)
220 SendMessage(hwndUserId, WM_SETTEXT, 0, (LPARAM)pwszUser);
221 else
222 SendMessage(hwndUserId, WM_SETTEXT, 0, (LPARAM)szUserFQDN);
223 }
224 if (hwndPassword)
225 SendMessage(hwndPassword, WM_SETTEXT, 0, (LPARAM)pwszPassword);
226
227 return VINF_SUCCESS; /** @todo */
228}
229
230/**
231 * Tries to retrieve credentials and enters them into the specified windows,
232 * optionally followed by a button press to confirm/abort the dialog.
233 *
234 * @return IPRT status code.
235 * @param hwndDlg Handle of dialog to enter credentials into.
236 * @param hwndUserId Handle of username text field. Optional.
237 * @param hwndPassword Handle of password text field. Optional.
238 * @param hwndDomain Handle of domain text field. Optional.
239 * @param wButtonToPress Button ID of dialog to press after successful
240 * retrieval + storage. If set to 0 no button will
241 * be pressed.
242 */
243int credentialsHandle(HWND hwndDlg,
244 HWND hwndUserId, HWND hwndPassword, HWND hwndDomain,
245 WORD wButtonToPress)
246{
247 int rc = VINF_SUCCESS;
248
249 if (!VBoxGINAHandleCurrentSession())
250 rc = VERR_NOT_FOUND;
251
252 if (RT_SUCCESS(rc))
253 {
254 rc = VbglR3CredentialsQueryAvailability();
255 if (RT_FAILURE(rc))
256 {
257 if (rc != VERR_NOT_FOUND)
258 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: error querying for credentials, rc=%Rrc\n", rc);
259 }
260 }
261
262 if (RT_SUCCESS(rc))
263 {
264 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: credentials available\n");
265
266 /*
267 * Set status to "terminating" to let the host know this module now
268 * tries to receive and use passed credentials so that credentials from
269 * the host won't be sent twice.
270 */
271 VBoxGINAReportStatus(VBoxGuestFacilityStatus_Terminating);
272
273 PRTUTF16 pwszUser, pwszPassword, pwszDomain;
274 rc = VbglR3CredentialsRetrieveUtf16(&pwszUser, &pwszPassword, &pwszDomain);
275 if (RT_SUCCESS(rc))
276 {
277 #ifdef DEBUG
278 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: retrieved credentials: user=%ls, password=%ls, domain=%ls\n",
279 pwszUser, pwszPassword, pwszDomain);
280 #else
281 VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: retrieved credentials: user=%ls, password=XXX, domain=%ls\n",
282 pwszUser, pwszDomain);
283 #endif
284 /* Fill in credentials to appropriate UI elements. */
285 rc = credentialsToUI(hwndDlg,
286 hwndUserId, hwndPassword, hwndDomain,
287 pwszUser, pwszPassword, pwszDomain);
288 if (RT_SUCCESS(rc))
289 {
290 /* Confirm/cancel the dialog by pressing the appropriate button. */
291 if (wButtonToPress)
292 {
293 WPARAM wParam = MAKEWPARAM(wButtonToPress, BN_CLICKED);
294 PostMessage(hwndDlg, WM_COMMAND, wParam, 0);
295 }
296 }
297
298 VbglR3CredentialsDestroyUtf16(pwszUser, pwszPassword, pwszDomain,
299 3 /* Passes */);
300 }
301 }
302
303 VBoxGINAVerbose(3, "VBoxGINA::credentialsHandle: returned with rc=%Rrc\n", rc);
304 return rc;
305}
306
307INT_PTR CALLBACK MyWlxLoggedOutSASDlgProc(HWND hwndDlg, // handle to dialog box
308 UINT uMsg, // message
309 WPARAM wParam, // first message parameter
310 LPARAM lParam) // second message parameter
311{
312 BOOL bResult;
313 static HWND s_hwndUserId, s_hwndPassword, s_hwndDomain = 0;
314
315 /*VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc\n");*/
316
317 //
318 // Pass on to MSGINA first.
319 //
320 bResult = g_pfnWlxLoggedOutSASDlgProc(hwndDlg, uMsg, wParam, lParam);
321
322 //
323 // We are only interested in the WM_INITDIALOG message.
324 //
325 switch (uMsg)
326 {
327 case WM_INITDIALOG:
328 {
329 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: got WM_INITDIALOG\n");
330
331 /* get the entry fields */
332 s_hwndUserId = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_USERNAME);
333 if (!s_hwndUserId)
334 s_hwndUserId = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_USERNAME2);
335 s_hwndPassword = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_PASSWORD);
336 if (!s_hwndPassword)
337 s_hwndPassword = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_PASSWORD2);
338 s_hwndDomain = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_DOMAIN);
339 if (!s_hwndDomain)
340 s_hwndDomain = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_DOMAIN2);
341
342 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: hwndUserId: %x, hwndPassword: %d, hwndDomain: %d\n",
343 s_hwndUserId, s_hwndPassword, s_hwndDomain);
344
345 /* terminate the credentials poller thread, it's done is job */
346 VBoxGINACredentialsPollerTerminate();
347
348 int rc = credentialsHandle(hwndDlg,
349 s_hwndUserId, s_hwndPassword, s_hwndDomain,
350 IDOK /* Button */);
351 if (RT_FAILURE(rc))
352 {
353 /*
354 * The dialog is there but we don't have any credentials.
355 * Create a timer and poll for them.
356 */
357 UINT_PTR uTimer = SetTimer(hwndDlg, IDT_LOGGEDONDLG_POLL, 200, NULL);
358 if (!uTimer)
359 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: failed creating timer! Last error: %ld\n",
360 GetLastError());
361 }
362 break;
363 }
364
365 case WM_TIMER:
366 {
367 /* is it our credentials poller timer? */
368 if (wParam == IDT_LOGGEDONDLG_POLL)
369 {
370 int rc = credentialsHandle(hwndDlg,
371 s_hwndUserId, s_hwndPassword, s_hwndDomain,
372 IDOK /* Button */);
373 if (RT_SUCCESS(rc))
374 {
375 /* we don't need the timer any longer */
376 KillTimer(hwndDlg, IDT_LOGGEDONDLG_POLL);
377 }
378 }
379 break;
380 }
381
382 case WM_DESTROY:
383 KillTimer(hwndDlg, IDT_LOGGEDONDLG_POLL);
384 break;
385 }
386 return bResult;
387}
388
389
390INT_PTR CALLBACK MyWlxLockedSASDlgProc(HWND hwndDlg, // handle to dialog box
391 UINT uMsg, // message
392 WPARAM wParam, // first message parameter
393 LPARAM lParam) // second message parameter
394{
395 BOOL bResult;
396 static HWND s_hwndPassword = 0;
397
398 /*VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc\n");*/
399
400 //
401 // Pass on to MSGINA first.
402 //
403 bResult = g_pfnWlxLockedSASDlgProc(hwndDlg, uMsg, wParam, lParam);
404
405 //
406 // We are only interested in the WM_INITDIALOG message.
407 //
408 switch (uMsg)
409 {
410 case WM_INITDIALOG:
411 {
412 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: WM_INITDIALOG\n");
413
414 /* get the entry fields */
415 s_hwndPassword = GetDlgItem(hwndDlg, IDC_WKSTALOCKED_PASSWORD);
416 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: hwndPassword: %d\n", s_hwndPassword);
417
418 /* terminate the credentials poller thread, it's done is job */
419 VBoxGINACredentialsPollerTerminate();
420
421 int rc = credentialsHandle(hwndDlg,
422 NULL /* Username */, s_hwndPassword, NULL /* Domain */,
423 IDOK /* Button */);
424 if (RT_FAILURE(rc))
425 {
426 /*
427 * The dialog is there but we don't have any credentials.
428 * Create a timer and poll for them.
429 */
430 UINT_PTR uTimer = SetTimer(hwndDlg, IDT_LOCKEDDLG_POLL, 200, NULL);
431 if (!uTimer)
432 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: failed creating timer! Last error: %ld\n",
433 GetLastError());
434 }
435 break;
436 }
437
438 case WM_TIMER:
439 {
440 /* is it our credentials poller timer? */
441 if (wParam == IDT_LOCKEDDLG_POLL)
442 {
443 int rc = credentialsHandle(hwndDlg,
444 NULL /* Username */, s_hwndPassword, NULL /* Domain */,
445 IDOK /* Button */);
446 if (RT_SUCCESS(rc))
447 {
448 /* we don't need the timer any longer */
449 KillTimer(hwndDlg, IDT_LOCKEDDLG_POLL);
450 }
451 }
452 break;
453 }
454
455 case WM_DESTROY:
456 {
457 VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: WM_DESTROY\n");
458
459 /* Because this is the only point where we know within our module that the locked
460 * dialog has been closed by a valid unlock password we have to set the appropriate
461 * facility status here. */
462 VBoxGINAReportStatus(VBoxGuestFacilityStatus_Terminated);
463
464 KillTimer(hwndDlg, IDT_LOCKEDDLG_POLL);
465 break;
466 }
467 }
468 return bResult;
469}
470
471
472int WINAPI MyWlxDialogBoxParam(HANDLE hWlx,
473 HANDLE hInst,
474 LPWSTR lpszTemplate,
475 HWND hwndOwner,
476 DLGPROC dlgprc,
477 LPARAM dwInitParam)
478{
479 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: lpszTemplate=%ls\n", lpszTemplate);
480
481 VBoxGINAReportStatus(VBoxGuestFacilityStatus_Active);
482
483 //
484 // We only know MSGINA dialogs by identifiers.
485 //
486 if (!HIWORD((int)(void*)lpszTemplate))
487 {
488 //
489 // Hook appropriate dialog boxes as necessary.
490 //
491 switch ((DWORD) lpszTemplate)
492 {
493 case IDD_WLXDIAPLAYSASNOTICE_DIALOG:
494 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: SAS notice dialog displayed; not handled\n");
495 break;
496
497 case IDD_WLXLOGGEDOUTSAS_DIALOG: /* Windows NT 4.0. */
498 case IDD_WLXLOGGEDOUTSAS_DIALOG2: /* Windows 2000 and up. */
499 {
500 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: returning hooked SAS logged out dialog\n");
501 g_pfnWlxLoggedOutSASDlgProc = dlgprc;
502 return g_pfnWlxDialogBoxParam(hWlx, hInst, lpszTemplate, hwndOwner,
503 MyWlxLoggedOutSASDlgProc, dwInitParam);
504 }
505
506 case IDD_SECURITY_DIALOG:
507 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: Security dialog displayed; not handled\n");
508 break;
509
510 case IDD_WLXWKSTALOCKEDSAS_DIALOG: /* Windows NT 4.0. */
511 case IDD_WLXWKSTALOCKEDSAS_DIALOG2: /* Windows 2000 and up. */
512 {
513 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: returning hooked SAS locked dialog\n");
514 g_pfnWlxLockedSASDlgProc = dlgprc;
515 return g_pfnWlxDialogBoxParam(hWlx, hInst, lpszTemplate, hwndOwner,
516 MyWlxLockedSASDlgProc, dwInitParam);
517 }
518
519 /** @todo Add other hooking stuff here. */
520
521 default:
522 VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: dialog %ld not handled\n", (DWORD)lpszTemplate);
523 break;
524 }
525 }
526
527 /* The rest will be redirected. */
528 return g_pfnWlxDialogBoxParam(hWlx, hInst, lpszTemplate,
529 hwndOwner, dlgprc, dwInitParam);
530}
531
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