For one of our customers, we created a custom credential provider which receives a decryption key and the filename of an encrypted file which container the username and password. This mechanism works perfectly for local user accounts. The user is authenticated when needed, and the old password is no longer accepted right after the user changes his password.
However, for windows live accounts the user can sometimes login using his old password after changing his password online (accounts.microsoft.com) and even after logging in to windows with the newly created password. Strange thing is, that the user cannot login by typing his old password. It only works when using the credential provider.
To make it more confusing, sometimes it works as expected and the behavior seems to differ from machine to machine.
My gut feeling tells me, there is something wrong with the code we use to authenticate the user, but I cannot figure out what is going wrong. We already tried to set the OldPasswordAllowedPeriod
registry value, but this seems not to work.
We use the following GetSerialization()
implementation, to fill the authentication buffer:
public int GetSerialization(...)
{
pcpgsr = _CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE.CPGSR_NO_CREDENTIAL_NOT_FINISHED;
pcpcs = new _CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION();
ppszOptionalStatusText = string.Empty;
pcpsiOptionalStatusIcon = _CREDENTIAL_PROVIDER_STATUS_ICON.CPSI_NONE;
try
{
var inCredSize = 0;
var inCredBuffer = Marshal.AllocCoTaskMem(0);
if (string.IsNullOrEmpty(_username) || _password == null || _password.Length == 0)
{
return SetAuthenticationError(out pcpgsr, out pcpsiOptionalStatusIcon, out ppszOptionalStatusText, "This NFC card has not been registered on this screen.");
}
if (!PInvoke.CredPackAuthenticationBuffer(0, _username, SecureStringToString(_password), inCredBuffer, ref inCredSize))
{
Marshal.FreeCoTaskMem(inCredBuffer);
inCredBuffer = Marshal.AllocCoTaskMem(inCredSize);
if (PInvoke.CredPackAuthenticationBuffer(0, _username, SecureStringToString(_password), inCredBuffer, ref inCredSize))
{
pcpgsr = _CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE.CPGSR_RETURN_CREDENTIAL_FINISHED;
pcpsiOptionalStatusIcon = _CREDENTIAL_PROVIDER_STATUS_ICON.CPSI_SUCCESS;
pcpcs.clsidCredentialProvider = Guid.Parse(Constants.CredentialProviderUID);
pcpcs.rgbSerialization = inCredBuffer;
pcpcs.cbSerialization = (uint)inCredSize;
RetrieveNegotiateAuthPackage(out var authPackage);
pcpcs.ulAuthenticationPackage = authPackage;
return HResult.S_OK;
}
_logger.LogError($"Failed to pack credentials for: {_username}.");
return SetAuthenticationError(out pcpgsr, out pcpsiOptionalStatusIcon, out ppszOptionalStatusText, "Failed to pack credentials.");
}
_logger.LogWarning("GetSerialization unexpectedly preliminary succesfully buffered credentials");
return SetAuthenticationError(out pcpgsr, out pcpsiOptionalStatusIcon, out ppszOptionalStatusText, "Something unexpected went wrong!");
}
catch (Exception ex)
{
// In case of any error, do not bring down winlogon
_logger.LogError(ex);
return SetAuthenticationError(out pcpgsr, out pcpsiOptionalStatusIcon, out ppszOptionalStatusText, "Something unexpected went wrong!");
}
finally
{
_shouldAutoLogin = false; // Block auto-login from going full-retard
}
}
Can someone point me in the right direction to solve this issue? Or, has someone any idea on what we are doing wrong when authenticating the user, using our custom credential provider?
Thanks in advance!