Mani Bharathy
Mani Bharathy

Reputation: 117

How to get new password from change password scenario in custom credential provider

I am doing a custom wrapped credential provider. In that i need to get the 'new password' field string from change password scenario. As far as I learnt, after user submits from the change password scenario, GetSerialization function in my credential provider is called and there i should be able to get values of the fields that the user has submitted. But I dont know exactly how to get it. I went through all over google and stack overflow but not able to get exactly what i need. Any help would be greatly appreciated.

HRESULT CSampleCredential::GetSerialization(
    CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
    CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs, 
    PWSTR* ppwszOptionalStatusText, 
    CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon
    )
 {
HRESULT hr = E_UNEXPECTED;


if (_pWrappedCredential != NULL)
{
    hr = _pWrappedCredential->GetSerialization(pcpgsr, pcpcs,         ppwszOptionalStatusText, pcpsiOptionalStatusIcon);
}

logger->log(NORMAL,L"****************GetSerialisation**************\n");

 return hr;
}

I'm getting the log "*******Get Serialization******" whenever user submits the logon/unlock form or change password form. There should be some way with that I can get the values of the fields(I'm interested in new password field). Either there should be some field id (so far i could not find one) with which i can access those values, or those values should be stored in some buffer which i should access and get the values or some other thing like that.

Upvotes: 4

Views: 1873

Answers (2)

Cosmic OM...
Cosmic OM...

Reputation: 291

If it helps anyone in future; here is how I did it. It's a my figured-out solution with appropriate code snippets from my working V1 (not V2) Wrapped Credential Provider(CP) - as tested on 64-bit Windows 7 and 8.1. (No guarantees on it working on all installations of Windows 7 and 8 though). Built the code using VS 2013 and VS 2010 successfully.

This is a slightly unorthodox way of determining the new password input - in a Change Password(CTRL-ALT-DEL) scenario. But, since my client wanted to track and check for the NEW password's complexity, as the user inputs/types it, it worked really well for me - but only on Windows 7 and 8/8.1; and NOT on Windows 10 - which happens to be a nightmare to implement custom "Change Password" Credential Providers for.

The way I did it, was to : a) check if the "cpus" variable in SetUsageScenario() in CredentialProvider.cpp was equal to CPUS_CHANGE_PASSWORD, and set a global boolean flag to TRUE, if so.

b) then, and this is the key part, in the function SetStringValue(), I kept track of each character input in the NEW password field, using the fieldID(which is the same, for all standard Windows 7/8 installations and default CP's that MS provides) - like this:

CSampleCredential::SetStringValue(
    DWORD dwFieldID,
    PCWSTR pwz
    )
{
    string strCurrentFieldData = ws2s(pwz);// this contains the current data , as typed by the user. SO, as the user keeps typing the password, this field will get longer and longer. Perfect!


    FILE_LOG(logINFO) << "in SetStringValue() FieldID = " << dwFieldID << "   Field value = " << strCurrentFieldData.c_str() ;
eWinVersion ver;
    char szVer[64] = { "" };
    WCHAR wszFullVersion[255] = { L"" };
    bool bSuccess = GetOSVersionString(wszFullVersion, 255, ver, szVer, 64);
    if (bSuccess)
    {
        if (ver == VER_WIN8 || ver == VER_WIN81  )
        {
            switch (dwFieldID)
            {
            case PFID_OLDPWD: csOldPassword = strCurrentFieldData;
                break;

            case PFID_NEWPWD: csNewPassword = strCurrentFieldData;
                break;

            case PFID_NEWPWDCONF: csNewPasswordConfirmation = strCurrentFieldData;
                break;
            default:
                break;
            }
        }else
            if (ver == VER_WIN7)
            {
                switch (dwFieldID)
                {
                case PFID_OLDPWD+1: csOldPassword = strCurrentFieldData;
                    break;

                case PFID_NEWPWD+1: csNewPassword = strCurrentFieldData;
                    break;

                case PFID_NEWPWDCONF+1: csNewPasswordConfirmation = strCurrentFieldData;
                    break;
                default:
                    break;
                }
            }
    }
    }
// and so on...
}

c) then, finally in Get CSampleCredential::Serialization(), I checked the password for complexity and returned the correct CP value, as appropriate - as follows:

//
// Collect the username and password into a serialized credential for the correct usage scenario 
// (logon/unlock is what's demonstrated in this sample).  LogonUI then passes these credentials 
// back to the system to log on.
//
HRESULT CSampleCredential::GetSerialization(
    CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
    CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs, 
    PWSTR* ppwszOptionalStatusText, 
    CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon
    )
{
    FILE_LOG(logINFO) << "In CSampleCredential::GetSerialization";

    HRESULT hr = E_UNEXPECTED;

    if (_pWrappedCredential != NULL)
    {
        hr = _pWrappedCredential->GetSerialization(pcpgsr, pcpcs, ppwszOptionalStatusText, pcpsiOptionalStatusIcon);
        if (g_bIsChangingPassword)
        {
            FILE_LOG(logINFO) << "********** In GetSerialization()..... Scenario = Change Password! ";
            char buf[256] = { "" };
            sprintf_s(buf, 256, "Old password Input = [%s] New password input = [%s]  New pwd confirmation inpot = [%s]",
                csOldPassword.c_str(), csNewPassword.c_str(), csNewPasswordConfirmation.c_str());
            FILE_LOG(logINFO) << buf;

            //******************************************
            // ************ IMPORTANT*****
            // If new password input by user doens't fulfil our complexity requirements, we don't allow password chnge to compelte, by CP
            if (!IsPasswordStrong(csNewPassword))
            {
                HWND hwndOwner = nullptr;
                _pCredProvCredentialEvents->OnCreatingWindow(&hwndOwner);

                *pcpgsr = CPGSR_NO_CREDENTIAL_NOT_FINISHED;
                ::MessageBox(hwndOwner, 
"The new password you have chosen is not secure enough.\n\rPlease try a more complex password that is:\n\ra) At least 8 characters long\r\nb)contains Upper case AND lower letters\n\rc)and numbers", 
"Insufficient Password Strength", 0);
            }
            //******************************************
        }
    }
    return hr;
}

This is provided to assist anyone who may need help with tracking NEW password fields in a WRAPPED Custom Credential Provider.

Upvotes: 1

Phil Sturges
Phil Sturges

Reputation: 183

The wrapped Credential serializes the new password into the CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION structure ready to be returned to Winlogon to present to the LSA. You can deserialize this structure using the CredUnPackAuthenticationBuffer function - and this will reveal what the new password is.

Upvotes: 1

Related Questions