/**
 * Reports the current auto-logon status to the host.
 *
 * This makes sure that the Failed state is sticky.
 *
 * @return  IPRT status code.
 * @param   enmStatus               Status to report to the host.
 */
VBGLR3DECL(int) VbglR3AutoLogonReportStatus(VBoxGuestFacilityStatus enmStatus)
{
    /*
     * VBoxGuestFacilityStatus_Failed is sticky.
     */
    static VBoxGuestFacilityStatus s_enmLastStatus = VBoxGuestFacilityStatus_Inactive;
    if (s_enmLastStatus != VBoxGuestFacilityStatus_Failed)
    {
        int rc = VbglR3ReportAdditionsStatus(VBoxGuestFacilityType_AutoLogon,
                                             enmStatus, 0 /* Flags */);
        if (rc == VERR_NOT_SUPPORTED)
        {
            /*
             * To maintain backwards compatibility to older hosts which don't have
             * VMMDevReportGuestStatus implemented we set the appropriate status via
             * guest property to have at least something.
             */
#ifdef VBOX_WITH_GUEST_PROPS
            uint32_t u32ClientId = 0;
            rc = VbglR3GuestPropConnect(&u32ClientId);
            if (RT_SUCCESS(rc))
            {
                const char *pszStatus;
                switch (enmStatus)
                {
                    case VBoxGuestFacilityStatus_Inactive:      pszStatus = "Inactive"; break;
                    case VBoxGuestFacilityStatus_Paused:        pszStatus = "Disabled"; break;
                    case VBoxGuestFacilityStatus_PreInit:       pszStatus = "PreInit"; break;
                    case VBoxGuestFacilityStatus_Init:          pszStatus = "Init"; break;
                    case VBoxGuestFacilityStatus_Active:        pszStatus = "Active"; break;
                    case VBoxGuestFacilityStatus_Terminating:   pszStatus = "Terminating"; break;
                    case VBoxGuestFacilityStatus_Terminated:    pszStatus = "Terminated"; break;
                    case VBoxGuestFacilityStatus_Failed:        pszStatus = "Failed"; break;
                    default:                                    pszStatus = NULL;
                }
                if (pszStatus)
                {
                    /*
                     * Use TRANSRESET when possible, fall back to TRANSIENT
                     * (generally sufficient unless the guest misbehaves).
                     */
                    static const char s_szPath[] = "/VirtualBox/GuestInfo/OS/AutoLogonStatus";
                    rc = VbglR3GuestPropWrite(u32ClientId, s_szPath, pszStatus, "TRANSRESET");
                    if (rc == VERR_PARSE_ERROR)
                        rc = VbglR3GuestPropWrite(u32ClientId, s_szPath, pszStatus, "TRANSIENT");
                }
                else
                    rc = VERR_INVALID_PARAMETER;

                VbglR3GuestPropDisconnect(u32ClientId);
            }
#endif
        }

        s_enmLastStatus = enmStatus;
    }
    return VINF_SUCCESS;
}
/** @copydoc VBOXSERVICE::pfnInit */
static DECLCALLBACK(int) VBoxServiceVMInfoInit(void)
{
    /*
     * If not specified, find the right interval default.
     * Then create the event sem to block on.
     */
    if (!g_cMsVMInfoInterval)
        g_cMsVMInfoInterval = g_DefaultInterval * 1000;
    if (!g_cMsVMInfoInterval)
        g_cMsVMInfoInterval = 10 * 1000;

    int rc = RTSemEventMultiCreate(&g_hVMInfoEvent);
    AssertRCReturn(rc, rc);

    VbglR3GetSessionId(&g_idVMInfoSession);
    /* The status code is ignored as this information is not available with VBox < 3.2.10. */

    rc = VbglR3GuestPropConnect(&g_uVMInfoGuestPropSvcClientID);
    if (RT_SUCCESS(rc))
        VBoxServiceVerbose(3, "VMInfo: Property Service Client ID: %#x\n", g_uVMInfoGuestPropSvcClientID);
    else
    {
        /* If the service was not found, we disable this service without
           causing VBoxService to fail. */
        if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
        {
            VBoxServiceVerbose(0, "VMInfo: Guest property service is not available, disabling the service\n");
            rc = VERR_SERVICE_DISABLED;
        }
        else
            VBoxServiceError("VMInfo: Failed to connect to the guest property service! Error: %Rrc\n", rc);
        RTSemEventMultiDestroy(g_hVMInfoEvent);
        g_hVMInfoEvent = NIL_RTSEMEVENTMULTI;
    }

    if (RT_SUCCESS(rc))
    {
        VBoxServicePropCacheCreate(&g_VMInfoPropCache, g_uVMInfoGuestPropSvcClientID);

        /*
         * Declare some guest properties with flags and reset values.
         */
        VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList",
                                        VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, NULL /* Delete on exit */);
        VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers",
                                        VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "0");
        VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers",
                                        VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "true");
        VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/Net/Count",
                                        VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE, NULL /* Delete on exit */);
    }
    return rc;
}
/**
 * Retrieves the mount root directory for auto-mounted shared
 * folders. mount point. If no string is set (VERR_NOT_FOUND)
 * it's up on the caller (guest) to decide where to mount.
 *
 * @returns VBox status code.
 * @param   ppszDir         Where to return the directory
 *                          string. This shall be freed by
 *                          calling RTStrFree.
 */
VBGLR3DECL(int) VbglR3SharedFolderGetMountDir(char **ppszDir)
{
    AssertPtrReturn(ppszDir, VERR_INVALID_POINTER);
    int rc;
#ifdef VBOX_WITH_GUEST_PROPS
    uint32_t u32ClientIdGuestProp;
    rc = VbglR3GuestPropConnect(&u32ClientIdGuestProp);
    if (RT_SUCCESS(rc))
    {
        rc = VbglR3GuestPropReadValueAlloc(u32ClientIdGuestProp, "/VirtualBox/GuestAdd/SharedFolders/MountDir", ppszDir);
        VbglR3GuestPropDisconnect(u32ClientIdGuestProp);
    }
#endif
    return rc;
}
/** @todo Move this part in VbglR3 and just provide a callback for the platform-specific
          notification stuff, since this is very similar to the VBoxClient code. */
int VBoxCheckHostVersion(void)
{
    int rc;
    uint32_t uGuestPropSvcClientID;

    rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
    if (RT_SUCCESS(rc))
    {
        char *pszHostVersion;
        char *pszGuestVersion;
        bool bUpdate;
        rc = VbglR3HostVersionCheckForUpdate(uGuestPropSvcClientID, &bUpdate, &pszHostVersion, &pszGuestVersion);
        if (RT_SUCCESS(rc))
        {
            if (bUpdate)
            {
                char szMsg[256]; /* Sizes according to MSDN. */
                char szTitle[64];

                /** @todo Add some translation macros here. */
                _snprintf(szTitle, sizeof(szTitle), "VirtualBox Guest Additions update available!");
                _snprintf(szMsg, sizeof(szMsg), "Your guest is currently running the Guest Additions version %s. "
                                                "We recommend updating to the latest version (%s) by choosing the "
                                                "install option from the Devices menu.", pszGuestVersion, pszHostVersion);

                rc = hlpShowBalloonTip(g_hInstance, g_hwndToolWindow, ID_TRAYICON,
                                       szMsg, szTitle,
                                       5000 /* Time to display in msec */, NIIF_INFO);
                if (RT_FAILURE(rc))
                    LogFlowFunc(("Guest Additions update found; however: could not show version notifier balloon tooltip, rc=%Rrc\n", rc));
            }

            /* Store host version to not notify again. */
            rc = VbglR3HostVersionLastCheckedStore(uGuestPropSvcClientID, pszHostVersion);

            VbglR3GuestPropReadValueFree(pszHostVersion);
            VbglR3GuestPropReadValueFree(pszGuestVersion);
        }
        VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
    }
    return rc;
}
/**
 * Retrieves the prefix for a shared folder mount point.  If no prefix
 * is set in the guest properties "sf_" is returned.
 *
 * @returns VBox status code.
 * @param   ppszPrefix      Where to return the prefix string.  This shall be
 *                          freed by calling RTStrFree.
 */
VBGLR3DECL(int) VbglR3SharedFolderGetMountPrefix(char **ppszPrefix)
{
    AssertPtrReturn(ppszPrefix, VERR_INVALID_POINTER);
    int rc;
#ifdef VBOX_WITH_GUEST_PROPS
    uint32_t u32ClientIdGuestProp;
    rc = VbglR3GuestPropConnect(&u32ClientIdGuestProp);
    if (RT_SUCCESS(rc))
    {
        rc = VbglR3GuestPropReadValueAlloc(u32ClientIdGuestProp, "/VirtualBox/GuestAdd/SharedFolders/MountPrefix", ppszPrefix);
        if (rc == VERR_NOT_FOUND) /* No prefix set? Then set the default. */
        {
#endif
            rc = RTStrDupEx(ppszPrefix, "sf_");
#ifdef VBOX_WITH_GUEST_PROPS
        }
        VbglR3GuestPropDisconnect(u32ClientIdGuestProp);
    }
#endif
    return rc;
}
/**
 * @interface_method_impl{VBOXSERVICE,pfnPreInit}
 */
static DECLCALLBACK(int) vgsvcTimeSyncPreInit(void)
{
#ifdef VBOX_WITH_GUEST_PROPS
    /** @todo Merge this function with vgsvcTimeSyncOption() to generalize
     *        the "command line args override guest property values" behavior. */

    /*
     * Read the service options from the VM's guest properties.
     * Note that these options can be overridden by the command line options later.
     */
    uint32_t uGuestPropSvcClientID;
    int rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
    if (RT_FAILURE(rc))
    {
        if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */
        {
            VGSvcVerbose(0, "VMInfo: Guest property service is not available, skipping\n");
            rc = VINF_SUCCESS;
        }
        else
            VGSvcError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
    }
    else
    {
        rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-interval",
                                 &g_TimeSyncInterval, 50, UINT32_MAX - 1);
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
            rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-min-adjust",
                                     &g_TimeSyncMinAdjust, 0, 3600000);
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
            rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-latency-factor",
                                     &g_TimeSyncLatencyFactor, 1, 1024);
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
            rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-max-latency",
                                     &g_TimeSyncMaxLatency, 1, 3600000);
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
            rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold",
                                     &g_TimeSyncSetThreshold, 0, 7*24*60*60*1000 /* a week */);
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
        {
            char *pszValue;
            rc = VGSvcReadProp(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-start",
                               &pszValue, NULL /* ppszFlags */, NULL /* puTimestamp */);
            if (RT_SUCCESS(rc))
            {
                g_fTimeSyncSetNext = true;
                RTStrFree(pszValue);
            }
        }
        if (   RT_SUCCESS(rc)
            || rc == VERR_NOT_FOUND)
        {
            uint32_t value;
            rc = VGSvcReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--timesync-set-on-restore",
                                     &value, 1, 1);
            if (RT_SUCCESS(rc))
                g_fTimeSyncSetOnRestore = !!value;
        }

        VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
    }

    if (rc == VERR_NOT_FOUND) /* If a value is not found, don't be sad! */
        rc = VINF_SUCCESS;
    return rc;
#else
    /* Nothing to do here yet. */
    return VINF_SUCCESS;
#endif
}
Example #7
0
/** @todo Move this part in VbglR3 and just provide a callback for the platform-specific
          notification stuff, since this is very similar to the VBoxTray code. */
static int run(struct VBCLSERVICE **ppInterface, bool fDaemonised)
{
    int rc;
    LogFlowFunc(("\n"));

    NOREF(ppInterface);
    /* Initialise the guest library. */
    rc = VbglR3InitUser();
    if (RT_FAILURE(rc))
        VBClFatalError(("Failed to connect to the VirtualBox kernel service, rc=%Rrc\n", rc));
    /* Because we need desktop notifications to be displayed, wait
     * some time to make the desktop environment load (as a work around). */
    if (fDaemonised)
        RTThreadSleep(30 * 1000 /* Wait 30 seconds */);

# ifdef VBOX_WITH_DBUS
    rc = RTDBusLoadLib();
    if (RT_FAILURE(rc))
        LogRel(("VBoxClient: D-Bus seems not to be installed; no host version check/notification done.\n"));
# else
    rc = VERR_NOT_IMPLEMENTED;
# endif /* VBOX_WITH_DBUS */

# ifdef VBOX_WITH_GUEST_PROPS
    uint32_t uGuestPropSvcClientID;
    if (RT_SUCCESS(rc))
    {
        rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
        if (RT_FAILURE(rc))
            LogRel(("VBoxClient: Cannot connect to guest property service while chcking for host version! rc = %Rrc\n", rc));
    }

    if (RT_SUCCESS(rc))
    {
        char *pszHostVersion;
        char *pszGuestVersion;
        bool bUpdate;

        rc = VbglR3HostVersionCheckForUpdate(uGuestPropSvcClientID, &bUpdate, &pszHostVersion, &pszGuestVersion);
        if (RT_SUCCESS(rc))
        {
            if (bUpdate)
            {
                char szMsg[1024];
                char szTitle[64];

                /** @todo add some translation macros here */
                RTStrPrintf(szTitle, sizeof(szTitle), "VirtualBox Guest Additions update available!");
#ifndef VBOX_OSE
                RTStrPrintf(szMsg, sizeof(szMsg), "Your guest is currently running the Guest Additions version %s. "
                                                  "We recommend updating to the latest version (%s) by choosing the "
                                                  "install option from the Devices menu.", pszGuestVersion, pszHostVersion);
#else
/* This is the message which appears for non-Oracle builds of the
* Guest Additions.  Distributors are encouraged to customise this. */
                RTStrPrintf(szMsg, sizeof(szMsg), "Your virtual machine is currently running the Guest Additions version %s. Since you are running a version of the Guest Additions provided by the operating system you installed in the virtual machine we recommend that you update it to at least version %s using that system's update features, or alternatively that you remove this version and then install the " VBOX_VENDOR_SHORT " Guest Additions package using the install option from the Devices menu. Please consult the documentation for the operating system you are running to find out how to update or remove the current Guest Additions package.", pszGuestVersion, pszHostVersion);
#endif
                rc = showNotify(szTitle, szMsg);
                LogRel(("VBoxClient: VirtualBox Guest Additions update available!"));
                if (RT_FAILURE(rc))
                    LogRel(("VBoxClient: Could not show version notifier tooltip! rc = %d\n", rc));
            }

            /* Store host version to not notify again */
            rc = VbglR3HostVersionLastCheckedStore(uGuestPropSvcClientID, pszHostVersion);

            VbglR3GuestPropReadValueFree(pszHostVersion);
            VbglR3GuestPropReadValueFree(pszGuestVersion);
        }
        VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
    }
# endif /* VBOX_WITH_GUEST_PROPS */
    VbglR3Term();
    LogFlowFunc(("returning %Rrc\n", rc));
    return rc;
}
DECLEXPORT(int) pam_sm_authenticate(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
{
    RT_NOREF1(iFlags);

    /* Parse arguments. */
    for (int i = 0; i < argc; i++)
    {
        if (!RTStrICmp(argv[i], "debug"))
            g_verbosity = 1;
        else
            pam_vbox_error(hPAM, "pam_vbox_authenticate: unknown command line argument \"%s\"\n", argv[i]);
    }
    pam_vbox_log(hPAM, "pam_vbox_authenticate called\n");

    int rc = pam_vbox_init(hPAM);
    if (RT_FAILURE(rc))
        return PAM_SUCCESS; /* Jump out as early as we can to not mess around. */

    bool fFallback = true;

#ifdef VBOX_WITH_GUEST_PROPS
    uint32_t uClientId;
    rc = VbglR3GuestPropConnect(&uClientId);
    if (RT_SUCCESS(rc))
    {
        char szVal[256];
        rc = pam_vbox_read_prop(hPAM, uClientId,
                                "/VirtualBox/GuestAdd/PAM/CredsWait",
                                true /* Read-only on guest */,
                                szVal, sizeof(szVal));
        if (RT_SUCCESS(rc))
        {
            /* All calls which are checked against rc2 are not critical, e.g. it does
             * not matter if they succeed or not. */
            uint32_t uTimeoutMS = RT_INDEFINITE_WAIT; /* Wait infinite by default. */
            int rc2 = pam_vbox_read_prop(hPAM, uClientId,
                                         "/VirtualBox/GuestAdd/PAM/CredsWaitTimeout",
                                         true /* Read-only on guest */,
                                         szVal, sizeof(szVal));
            if (RT_SUCCESS(rc2))
            {
                uTimeoutMS = RTStrToUInt32(szVal);
                if (!uTimeoutMS)
                {
                    pam_vbox_error(hPAM, "pam_vbox_authenticate: invalid waiting timeout value specified, defaulting to infinite timeout\n");
                    uTimeoutMS = RT_INDEFINITE_WAIT;
                }
                else
                    uTimeoutMS = uTimeoutMS * 1000; /* Make ms out of s. */
            }

            rc2 = pam_vbox_read_prop(hPAM, uClientId,
                                     "/VirtualBox/GuestAdd/PAM/CredsMsgWaiting",
                                     true /* Read-only on guest */,
                                     szVal, sizeof(szVal));
            const char *pszWaitMsg = NULL;
            if (RT_SUCCESS(rc2))
                pszWaitMsg = szVal;

            rc2 = vbox_set_msg(hPAM, 0 /* Info message */,
                               pszWaitMsg ? pszWaitMsg : "Waiting for credentials ...");
            if (RT_FAILURE(rc2)) /* Not critical. */
                pam_vbox_error(hPAM, "pam_vbox_authenticate: error setting waiting information message, rc=%Rrc\n", rc2);

            if (RT_SUCCESS(rc))
            {
                /* Before we actuall wait for credentials just make sure we didn't already get credentials
                 * set so that we can skip waiting for them ... */
                rc = pam_vbox_check_creds(hPAM);
                if (rc == VERR_NOT_FOUND)
                {
                    rc = pam_vbox_wait_for_creds(hPAM, uClientId, uTimeoutMS);
                    if (rc == VERR_TIMEOUT)
                    {
                        pam_vbox_log(hPAM, "pam_vbox_authenticate: no credentials given within time\n");

                        rc2 = pam_vbox_read_prop(hPAM, uClientId,
                                                 "/VirtualBox/GuestAdd/PAM/CredsMsgWaitTimeout",
                                                 true /* Read-only on guest */,
                                                 szVal, sizeof(szVal));
                        if (RT_SUCCESS(rc2))
                        {
                            rc2 = vbox_set_msg(hPAM, 0 /* Info message */, szVal);
                            AssertRC(rc2);
                        }
                    }
                    else if (rc == VERR_CANCELLED)
                    {
                        pam_vbox_log(hPAM, "pam_vbox_authenticate: waiting aborted\n");

                        rc2 = pam_vbox_read_prop(hPAM, uClientId,
                                                 "/VirtualBox/GuestAdd/PAM/CredsMsgWaitAbort",
                                                 true /* Read-only on guest */,
                                                 szVal, sizeof(szVal));
                        if (RT_SUCCESS(rc2))
                        {
                            rc2 = vbox_set_msg(hPAM, 0 /* Info message */, szVal);
                            AssertRC(rc2);
                        }
                    }
                }

                /* If we got here we don't need the fallback, so just deactivate it. */
                fFallback = false;
            }
        }

        VbglR3GuestPropDisconnect(uClientId);
    }
#endif /* VBOX_WITH_GUEST_PROPS */

    if (fFallback)
    {
        pam_vbox_log(hPAM, "pam_vbox_authenticate: falling back to old method\n");

        /* If anything went wrong in the code above we just do a credentials
         * check like it was before: Try retrieving the stuff and authenticating. */
        int rc2 = pam_vbox_check_creds(hPAM);
        if (RT_SUCCESS(rc))
            rc = rc2;
    }

    pam_vbox_shutdown(hPAM);

    pam_vbox_log(hPAM, "pam_vbox_authenticate: overall result rc=%Rrc\n", rc);

    /* Never report an error here because if no credentials from the host are available or something
     * went wrong we then let do the authentication by the next module in the stack. */

    /* We report success here because this is all we can do right now -- we passed the credentials
     * to the next PAM module in the block above which then might do a shadow (like pam_unix/pam_unix2)
     * password verification to "really" authenticate the user. */
    return PAM_SUCCESS;
}
/**
 * Thread function waiting for credentials to arrive.
 *
 * @return  IPRT status code.
 * @param   hThreadSelf             Thread handle.
 * @param   pvUser                  Pointer to a PAMVBOXTHREAD structure providing
 *                                  required data used / set by the thread.
 */
static DECLCALLBACK(int) pam_vbox_wait_thread(RTTHREAD hThreadSelf, void *pvUser)
{
    RT_NOREF1(hThreadSelf);
    PPAMVBOXTHREAD pUserData = (PPAMVBOXTHREAD)pvUser;
    AssertPtr(pUserData);

    int rc = VINF_SUCCESS;
    /* Get current time stamp to later calculate rest of timeout left. */
    uint64_t u64StartMS = RTTimeMilliTS();

#ifdef VBOX_WITH_GUEST_PROPS
    uint32_t uClientID = 0;
    rc = VbglR3GuestPropConnect(&uClientID);
    if (RT_FAILURE(rc))
    {
        pam_vbox_error(pUserData->hPAM, "pam_vbox_wait_thread: Unable to connect to guest property service, rc=%Rrc\n", rc);
    }
    else
    {
        pam_vbox_log(pUserData->hPAM, "pam_vbox_wait_thread: clientID=%u\n", uClientID);
#endif
        for (;;)
        {
#ifdef VBOX_WITH_GUEST_PROPS
            if (uClientID)
            {
                rc = pam_vbox_wait_prop(pUserData->hPAM, uClientID,
                                        "/VirtualBox/GuestAdd/PAM/CredsWaitAbort",
                                        500 /* Wait 500ms, same as VBoxGINA/VBoxCredProv. */);
                switch (rc)
                {
                    case VINF_SUCCESS:
                        /* Somebody (guest/host) wants to abort waiting for credentials. */
                        break;

                    case VERR_INTERRUPTED:
                        pam_vbox_error(pUserData->hPAM, "pam_vbox_wait_thread: The abort notification request timed out or was interrupted\n");
                        break;

                    case VERR_TIMEOUT:
                        /* We did not receive an abort message within time. */
                        break;

                    case VERR_TOO_MUCH_DATA:
                        pam_vbox_error(pUserData->hPAM, "pam_vbox_wait_thread: Temporarily unable to get abort notification\n");
                        break;

                    default:
                        pam_vbox_error(pUserData->hPAM, "pam_vbox_wait_thread: The abort notification request failed with rc=%Rrc\n", rc);
                        break;
                }

                if (RT_SUCCESS(rc)) /* Abort waiting. */
                {
                    pam_vbox_log(pUserData->hPAM, "pam_vbox_wait_thread: Got notification to abort waiting\n");
                    rc = VERR_CANCELLED;
                    break;
                }
            }
#endif
            if (   RT_SUCCESS(rc)
                || rc == VERR_TIMEOUT)
            {
                rc = pam_vbox_check_creds(pUserData->hPAM);
                if (RT_SUCCESS(rc))
                {
                    /* Credentials retrieved. */
                    break; /* Thread no longer is required, bail out. */
                }
                else if (rc == VERR_NOT_FOUND)
                {
                    /* No credentials found, but try next round (if there's
                     * time left for) ... */
#ifndef VBOX_WITH_GUEST_PROPS
                    RTThreadSleep(500); /* Wait 500 ms. */
#endif
                }
                else
                    break; /* Something bad happend ... */
            }
            else
                break;

            /* Calculate timeout value left after process has been started.  */
            uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS;
            /* Is it time to bail out? */
            if (pUserData->uTimeoutMS < u64Elapsed)
            {
                pam_vbox_log(pUserData->hPAM, "pam_vbox_wait_thread: Waiting thread has reached timeout (%dms), exiting ...\n",
                             pUserData->uTimeoutMS);
                rc = VERR_TIMEOUT;
                break;
            }
        }
#ifdef VBOX_WITH_GUEST_PROPS
    }
    VbglR3GuestPropDisconnect(uClientID);
#endif

    /* Save result. */
    pUserData->rc = rc; /** @todo Use ASMAtomicXXX? */

    int rc2 = RTThreadUserSignal(RTThreadSelf());
    AssertRC(rc2);

    pam_vbox_log(pUserData->hPAM, "pam_vbox_wait_thread: Waiting thread returned with rc=%Rrc\n", rc);
    return rc;
}