int VBoxGINACredentialsPollerTerminate(void) { if (gThreadPoller == NIL_RTTHREAD) return VINF_SUCCESS; VBoxGINAVerbose(0, "VBoxGINA::credentialsPollerTerminate\n"); /* post termination event semaphore */ int rc = RTThreadUserSignal(gThreadPoller); if (RT_SUCCESS(rc)) { VBoxGINAVerbose(0, "VBoxGINA::credentialsPollerTerminate: waiting for thread to terminate\n"); rc = RTThreadWait(gThreadPoller, RT_INDEFINITE_WAIT, NULL); } else VBoxGINAVerbose(0, "VBoxGINA::credentialsPollerTerminate: thread has terminated? wait rc = %Rrc\n", rc); if (RT_SUCCESS(rc)) { gThreadPoller = NIL_RTTHREAD; } VBoxGINAVerbose(0, "VBoxGINA::credentialsPollerTerminate: returned with rc=%Rrc)\n", rc); return rc; }
/** * Reports VBoxGINA's status to the host (treated as a guest facility). * * @return IPRT status code. * @param enmStatus Status to report to the host. */ int VBoxGINAReportStatus(VBoxGuestFacilityStatus enmStatus) { VBoxGINAVerbose(0, "VBoxGINA: reporting status %d\n", enmStatus); int rc = VbglR3AutoLogonReportStatus(enmStatus); if (RT_FAILURE(rc)) VBoxGINAVerbose(0, "VBoxGINA: failed to report status %d, rc=%Rrc\n", enmStatus, rc); return rc; }
void hookDialogBoxes(PVOID pWinlogonFunctions, DWORD dwWlxVersion) { if (!pWinlogonFunctions) /* Needed for testcase. */ return; VBoxGINAVerbose(0, "VBoxGINA::hookDialogBoxes\n"); /* this is version dependent */ switch (dwWlxVersion) { case WLX_VERSION_1_0: { g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_0)pWinlogonFunctions)->WlxDialogBoxParam; ((PWLX_DISPATCH_VERSION_1_0)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam; break; } case WLX_VERSION_1_1: { g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_1)pWinlogonFunctions)->WlxDialogBoxParam; ((PWLX_DISPATCH_VERSION_1_1)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam; break; } case WLX_VERSION_1_2: { g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_2)pWinlogonFunctions)->WlxDialogBoxParam; ((PWLX_DISPATCH_VERSION_1_2)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam; break; } case WLX_VERSION_1_3: { g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_3)pWinlogonFunctions)->WlxDialogBoxParam; ((PWLX_DISPATCH_VERSION_1_3)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam; break; } case WLX_VERSION_1_4: { g_pfnWlxDialogBoxParam = ((PWLX_DISPATCH_VERSION_1_4)pWinlogonFunctions)->WlxDialogBoxParam; ((PWLX_DISPATCH_VERSION_1_4)pWinlogonFunctions)->WlxDialogBoxParam = MyWlxDialogBoxParam; break; } default: { VBoxGINAVerbose(0, "VBoxGINA::hookDialogBoxes: unrecognized version '%d', nothing hooked!\n", dwWlxVersion); /* not good, don't do anything */ break; } } }
int VBoxGINACredentialsPollerCreate(void) { if (!VBoxGINAHandleCurrentSession()) return VINF_SUCCESS; VBoxGINAVerbose(0, "VBoxGINA::credentialsPollerCreate\n"); /* don't create more than one of them */ if (gThreadPoller != NIL_RTTHREAD) { VBoxGINAVerbose(0, "VBoxGINA::credentialsPollerCreate: thread already running, returning!\n"); return VINF_SUCCESS; } /* create the poller thread */ int rc = RTThreadCreate(&gThreadPoller, credentialsPoller, NULL, 0, RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "creds"); if (RT_FAILURE(rc)) VBoxGINAVerbose(0, "VBoxGINA::credentialsPollerCreate: failed to create thread, rc = %Rrc\n", rc); return rc; }
/** * Determines whether we should handle the current session or not. * * @return bool true if we should handle this session, false if not. */ bool VBoxGINAHandleCurrentSession(void) { /* Load global configuration from registry. */ int rc = VBoxGINALoadConfiguration(); if (RT_FAILURE(rc)) VBoxGINAVerbose(0, "VBoxGINA::handleCurrentSession: Error loading global configuration, rc=%Rrc\n", rc); bool fHandle = false; if (VbglR3AutoLogonIsRemoteSession()) { if (g_dwHandleRemoteSessions) /* Force remote session handling. */ fHandle = true; } else /* No remote session. */ fHandle = true; #ifdef DEBUG VBoxGINAVerbose(3, "VBoxGINA::handleCurrentSession: Handling current session=%RTbool\n", fHandle); #endif return fHandle; }
int WINAPI MyWlxDialogBoxParam(HANDLE hWlx, HANDLE hInst, LPWSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc, LPARAM dwInitParam) { VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: lpszTemplate=%ls\n", lpszTemplate); VBoxGINAReportStatus(VBoxGuestFacilityStatus_Active); // // We only know MSGINA dialogs by identifiers. // if (!HIWORD((int)(void*)lpszTemplate)) { // // Hook appropriate dialog boxes as necessary. // switch ((DWORD) lpszTemplate) { case IDD_WLXDIAPLAYSASNOTICE_DIALOG: VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: SAS notice dialog displayed; not handled\n"); break; case IDD_WLXLOGGEDOUTSAS_DIALOG: /* Windows NT 4.0. */ case IDD_WLXLOGGEDOUTSAS_DIALOG2: /* Windows 2000 and up. */ { VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: returning hooked SAS logged out dialog\n"); g_pfnWlxLoggedOutSASDlgProc = dlgprc; return g_pfnWlxDialogBoxParam(hWlx, hInst, lpszTemplate, hwndOwner, MyWlxLoggedOutSASDlgProc, dwInitParam); } case IDD_SECURITY_DIALOG: VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: Security dialog displayed; not handled\n"); break; case IDD_WLXWKSTALOCKEDSAS_DIALOG: /* Windows NT 4.0. */ case IDD_WLXWKSTALOCKEDSAS_DIALOG2: /* Windows 2000 and up. */ { VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: returning hooked SAS locked dialog\n"); g_pfnWlxLockedSASDlgProc = dlgprc; return g_pfnWlxDialogBoxParam(hWlx, hInst, lpszTemplate, hwndOwner, MyWlxLockedSASDlgProc, dwInitParam); } /** @todo Add other hooking stuff here. */ default: VBoxGINAVerbose(0, "VBoxGINA::MyWlxDialogBoxParam: dialog %ld not handled\n", (DWORD)lpszTemplate); break; } } /* The rest will be redirected. */ return g_pfnWlxDialogBoxParam(hWlx, hInst, lpszTemplate, hwndOwner, dlgprc, dwInitParam); }
/** * Poller thread. Checks periodically whether there are credentials. */ static DECLCALLBACK(int) credentialsPoller(RTTHREAD ThreadSelf, void *pvUser) { RT_NOREF(pvUser); VBoxGINAVerbose(0, "VBoxGINA::credentialsPoller\n"); do { int rc = VbglR3CredentialsQueryAvailability(); if (RT_SUCCESS(rc)) { VBoxGINAVerbose(0, "VBoxGINA::credentialsPoller: got credentials, simulating C-A-D\n"); /* tell WinLogon to start the attestation process */ pWlxFuncs->WlxSasNotify(hGinaWlx, WLX_SAS_TYPE_CTRL_ALT_DEL); /* time to say goodbye */ return 0; } if ( RT_FAILURE(rc) && rc != VERR_NOT_FOUND) { static int s_cBitchedQueryAvail = 0; if (s_cBitchedQueryAvail++ < 5) VBoxGINAVerbose(0, "VBoxGINA::credentialsPoller: querying for credentials failed with rc=%Rrc\n", rc); } /* wait a bit */ if (RTThreadUserWait(ThreadSelf, 500) == VINF_SUCCESS) { VBoxGINAVerbose(0, "VBoxGINA::credentialsPoller: we were asked to terminate\n"); /* we were asked to terminate, do that instantly! */ return 0; } } while (1); return 0; }
INT_PTR CALLBACK MyWlxLockedSASDlgProc(HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam) // second message parameter { BOOL bResult; static HWND s_hwndPassword = 0; /*VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc\n");*/ // // Pass on to MSGINA first. // bResult = g_pfnWlxLockedSASDlgProc(hwndDlg, uMsg, wParam, lParam); // // We are only interested in the WM_INITDIALOG message. // switch (uMsg) { case WM_INITDIALOG: { VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: WM_INITDIALOG\n"); /* get the entry fields */ s_hwndPassword = GetDlgItem(hwndDlg, IDC_WKSTALOCKED_PASSWORD); VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: hwndPassword: %d\n", s_hwndPassword); /* terminate the credentials poller thread, it's done is job */ VBoxGINACredentialsPollerTerminate(); int rc = credentialsHandle(hwndDlg, NULL /* Username */, s_hwndPassword, NULL /* Domain */, IDOK /* Button */); if (RT_FAILURE(rc)) { /* * The dialog is there but we don't have any credentials. * Create a timer and poll for them. */ UINT_PTR uTimer = SetTimer(hwndDlg, IDT_LOCKEDDLG_POLL, 200, NULL); if (!uTimer) VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: failed creating timer! Last error: %ld\n", GetLastError()); } break; } case WM_TIMER: { /* is it our credentials poller timer? */ if (wParam == IDT_LOCKEDDLG_POLL) { int rc = credentialsHandle(hwndDlg, NULL /* Username */, s_hwndPassword, NULL /* Domain */, IDOK /* Button */); if (RT_SUCCESS(rc)) { /* we don't need the timer any longer */ KillTimer(hwndDlg, IDT_LOCKEDDLG_POLL); } } break; } case WM_DESTROY: { VBoxGINAVerbose(0, "VBoxGINA::MyWlxLockedSASDlgProc: WM_DESTROY\n"); /* Because this is the only point where we know within our module that the locked * dialog has been closed by a valid unlock password we have to set the appropriate * facility status here. */ VBoxGINAReportStatus(VBoxGuestFacilityStatus_Terminated); KillTimer(hwndDlg, IDT_LOCKEDDLG_POLL); break; } } return bResult; }
INT_PTR CALLBACK MyWlxLoggedOutSASDlgProc(HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam) // second message parameter { BOOL bResult; static HWND s_hwndUserId, s_hwndPassword, s_hwndDomain = 0; /*VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc\n");*/ // // Pass on to MSGINA first. // bResult = g_pfnWlxLoggedOutSASDlgProc(hwndDlg, uMsg, wParam, lParam); // // We are only interested in the WM_INITDIALOG message. // switch (uMsg) { case WM_INITDIALOG: { VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: got WM_INITDIALOG\n"); /* get the entry fields */ s_hwndUserId = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_USERNAME); if (!s_hwndUserId) s_hwndUserId = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_USERNAME2); s_hwndPassword = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_PASSWORD); if (!s_hwndPassword) s_hwndPassword = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_PASSWORD2); s_hwndDomain = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_DOMAIN); if (!s_hwndDomain) s_hwndDomain = GetDlgItem(hwndDlg, IDC_WLXLOGGEDOUTSAS_DOMAIN2); VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: hwndUserId: %x, hwndPassword: %d, hwndDomain: %d\n", s_hwndUserId, s_hwndPassword, s_hwndDomain); /* terminate the credentials poller thread, it's done is job */ VBoxGINACredentialsPollerTerminate(); int rc = credentialsHandle(hwndDlg, s_hwndUserId, s_hwndPassword, s_hwndDomain, IDOK /* Button */); if (RT_FAILURE(rc)) { /* * The dialog is there but we don't have any credentials. * Create a timer and poll for them. */ UINT_PTR uTimer = SetTimer(hwndDlg, IDT_LOGGEDONDLG_POLL, 200, NULL); if (!uTimer) VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: failed creating timer! Last error: %ld\n", GetLastError()); } break; } case WM_TIMER: { /* is it our credentials poller timer? */ if (wParam == IDT_LOGGEDONDLG_POLL) { int rc = credentialsHandle(hwndDlg, s_hwndUserId, s_hwndPassword, s_hwndDomain, IDOK /* Button */); if (RT_SUCCESS(rc)) { /* we don't need the timer any longer */ KillTimer(hwndDlg, IDT_LOGGEDONDLG_POLL); } } break; } case WM_DESTROY: KillTimer(hwndDlg, IDT_LOGGEDONDLG_POLL); break; } return bResult; }
/** * Tries to retrieve credentials and enters them into the specified windows, * optionally followed by a button press to confirm/abort the dialog. * * @return IPRT status code. * @param hwndDlg Handle of dialog to enter credentials into. * @param hwndUserId Handle of username text field. Optional. * @param hwndPassword Handle of password text field. Optional. * @param hwndDomain Handle of domain text field. Optional. * @param wButtonToPress Button ID of dialog to press after successful * retrieval + storage. If set to 0 no button will * be pressed. */ int credentialsHandle(HWND hwndDlg, HWND hwndUserId, HWND hwndPassword, HWND hwndDomain, WORD wButtonToPress) { int rc = VINF_SUCCESS; if (!VBoxGINAHandleCurrentSession()) rc = VERR_NOT_FOUND; if (RT_SUCCESS(rc)) { rc = VbglR3CredentialsQueryAvailability(); if (RT_FAILURE(rc)) { if (rc != VERR_NOT_FOUND) VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: error querying for credentials, rc=%Rrc\n", rc); } } if (RT_SUCCESS(rc)) { VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: credentials available\n"); /* * Set status to "terminating" to let the host know this module now * tries to receive and use passed credentials so that credentials from * the host won't be sent twice. */ VBoxGINAReportStatus(VBoxGuestFacilityStatus_Terminating); PRTUTF16 pwszUser, pwszPassword, pwszDomain; rc = VbglR3CredentialsRetrieveUtf16(&pwszUser, &pwszPassword, &pwszDomain); if (RT_SUCCESS(rc)) { #ifdef DEBUG VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: retrieved credentials: user=%ls, password=%ls, domain=%ls\n", pwszUser, pwszPassword, pwszDomain); #else VBoxGINAVerbose(0, "VBoxGINA::credentialsHandle: retrieved credentials: user=%ls, password=XXX, domain=%ls\n", pwszUser, pwszDomain); #endif /* Fill in credentials to appropriate UI elements. */ rc = credentialsToUI(hwndDlg, hwndUserId, hwndPassword, hwndDomain, pwszUser, pwszPassword, pwszDomain); if (RT_SUCCESS(rc)) { /* Confirm/cancel the dialog by pressing the appropriate button. */ if (wButtonToPress) { WPARAM wParam = MAKEWPARAM(wButtonToPress, BN_CLICKED); PostMessage(hwndDlg, WM_COMMAND, wParam, 0); } } VbglR3CredentialsDestroyUtf16(pwszUser, pwszPassword, pwszDomain, 3 /* Passes */); } } VBoxGINAVerbose(3, "VBoxGINA::credentialsHandle: returned with rc=%Rrc\n", rc); return rc; }
/** * Enters credentials into the given text fields. * * @return IPRT status code. * @param hwndDlg Handle of dialog to enter credentials into. * @param hwndUserId Handle of username text field. Optional. * @param hwndPassword Handle of password text field. Optional. * @param hwndDomain Handle of domain text field. Optional. * @param pwszUser Username to enter into username text field. * @param pwszPassword Password to enter into password text field. * @param pwszDomain Domain to enter into domain text field. */ int credentialsToUI(HWND hwndDlg, HWND hwndUserId, HWND hwndPassword, HWND hwndDomain, PCRTUTF16 pwszUser, PCRTUTF16 pwszPassword, PCRTUTF16 pwszDomain) { BOOL bIsFQDN = FALSE; wchar_t szUserFQDN[512]; /* VMMDEV_CREDENTIALS_STRLEN + 255 bytes max. for FQDN */ if (hwndDomain) { /* search the domain combo box for our required domain and select it */ VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Trying to find domain entry in combo box ...\n"); DWORD dwIndex = (DWORD) SendMessage(hwndDomain, CB_FINDSTRING, 0, (LPARAM)pwszDomain); if (dwIndex != CB_ERR) { VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Found domain at combo box pos %ld\n", dwIndex); SendMessage(hwndDomain, CB_SETCURSEL, (WPARAM) dwIndex, 0); EnableWindow(hwndDomain, FALSE); } else { VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain not found in combo box ...\n"); /* If the domain value has a dot (.) in it, it is a FQDN (Fully Qualified Domain Name) * which will not work with the combo box selection because Windows only keeps the * NETBIOS names to the left most part of the domain name there. Of course a FQDN * then will not be found by the search in the block above. * * To solve this problem the FQDN domain value will be appended at the user name value * (Kerberos style) using an "@", e.g. "<user-name>@full.qualified.domain". * */ size_t l = wcslen(pwszDomain); if (l > 255) VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Warning! FQDN (domain) is too long (max 255 bytes), will be truncated!\n"); if (wcslen(pwszUser) > 0) /* We need a user name that we can use in caes of a FQDN */ { if (l > 16) /* Domain name is longer than 16 chars, cannot be a NetBIOS name anymore */ { VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain seems to be a FQDN (length)!\n"); bIsFQDN = TRUE; } else if ( l > 0 && wcsstr(pwszDomain, L".") != NULL) /* if we found a dot (.) in the domain name, this has to be a FQDN */ { VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: Domain seems to be a FQDN (dot)!\n"); bIsFQDN = TRUE; } if (bIsFQDN) { swprintf(szUserFQDN, sizeof(szUserFQDN) / sizeof(wchar_t), L"%s@%s", pwszUser, pwszDomain); VBoxGINAVerbose(0, "VBoxGINA::MyWlxLoggedOutSASDlgProc: FQDN user name is now: %s!\n", szUserFQDN); } } } } if (hwndUserId) { if (!bIsFQDN) SendMessage(hwndUserId, WM_SETTEXT, 0, (LPARAM)pwszUser); else SendMessage(hwndUserId, WM_SETTEXT, 0, (LPARAM)szUserFQDN); } if (hwndPassword) SendMessage(hwndPassword, WM_SETTEXT, 0, (LPARAM)pwszPassword); return VINF_SUCCESS; /** @todo */ }