如何从自定义凭据提供程序中的更改密码场景中获取新密码
How to get new password from change password scenario in custom credential provider
我正在做一个自定义包装的凭据提供程序。因为我需要从更改密码场景中获取 'new password' 字段字符串。据我所知,在用户从更改密码场景提交后,我的凭据提供程序中的 GetSerialization 函数被调用,我应该能够获得用户提交的字段的值。但我不知道如何得到它。我遍历了 google 和堆栈溢出,但无法得到我需要的东西。任何帮助将不胜感激。
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;
}
每当用户提交 logon/unlock 表单或更改密码表单时,我都会收到日志“*******Get Serialization******”。应该有一些方法可以让我获得字段的值(我对新密码字段感兴趣)。要么应该有一些字段 id(到目前为止我找不到),我可以用它来访问这些值,或者这些值应该存储在一些缓冲区中,我应该访问它并获取这些值或类似的东西。
包装的凭据将新密码序列化为 CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION 结构,准备返回到 Winlogon 以呈现给 LSA。您可以使用 CredUnPackAuthenticationBuffer 函数反序列化此结构 - 这将揭示新密码是什么。
如果对以后的人有帮助;我是这样做的。这是我想出的解决方案,其中包含来自我工作的 V1(而非 V2)Wrapped Credential Provider(CP) 的适当代码片段 - 在 64 位 Windows 7 和 8.1 上进行了测试。 (虽然不能保证它适用于 Windows 7 和 8 的所有安装)。使用 VS 2013 和 VS 2010 成功构建代码。
这是在更改密码 (CTRL-ALT-DEL) 场景中确定新密码输入的一种稍微非正统的方法。但是,由于我的客户想要跟踪和检查新密码的复杂性,作为用户 inputs/types 它,它对我来说非常有效 - 但仅限于 Windows 7 和 8/8.1;而不是 Windows 10 - 这恰好是实现自定义 "Change Password" 凭据提供程序的噩梦。
我的做法是:
a) 检查 CredentialProvider.cpp 中 SetUsageScenario() 中的 "cpus" 变量是否等于 CPUS_CHANGE_PASSWORD,如果是,则将全局布尔标志设置为 TRUE。
b) 然后,这是关键部分,在函数 SetStringValue() 中,我使用 fieldID(对于所有标准 Windows 7/8 安装和 MS 提供的默认 CP)- 像这样:
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) 然后,最后在 Get CSampleCredential::Serialization() 中,我检查了密码的复杂性,并根据需要返回了正确的 CP 值——如下:
//
// 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;
}
提供此信息是为了帮助可能需要帮助的任何人跟踪 WRAPPED 自定义凭据提供程序中的新密码字段。
我正在做一个自定义包装的凭据提供程序。因为我需要从更改密码场景中获取 'new password' 字段字符串。据我所知,在用户从更改密码场景提交后,我的凭据提供程序中的 GetSerialization 函数被调用,我应该能够获得用户提交的字段的值。但我不知道如何得到它。我遍历了 google 和堆栈溢出,但无法得到我需要的东西。任何帮助将不胜感激。
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;
}
每当用户提交 logon/unlock 表单或更改密码表单时,我都会收到日志“*******Get Serialization******”。应该有一些方法可以让我获得字段的值(我对新密码字段感兴趣)。要么应该有一些字段 id(到目前为止我找不到),我可以用它来访问这些值,或者这些值应该存储在一些缓冲区中,我应该访问它并获取这些值或类似的东西。
包装的凭据将新密码序列化为 CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION 结构,准备返回到 Winlogon 以呈现给 LSA。您可以使用 CredUnPackAuthenticationBuffer 函数反序列化此结构 - 这将揭示新密码是什么。
如果对以后的人有帮助;我是这样做的。这是我想出的解决方案,其中包含来自我工作的 V1(而非 V2)Wrapped Credential Provider(CP) 的适当代码片段 - 在 64 位 Windows 7 和 8.1 上进行了测试。 (虽然不能保证它适用于 Windows 7 和 8 的所有安装)。使用 VS 2013 和 VS 2010 成功构建代码。
这是在更改密码 (CTRL-ALT-DEL) 场景中确定新密码输入的一种稍微非正统的方法。但是,由于我的客户想要跟踪和检查新密码的复杂性,作为用户 inputs/types 它,它对我来说非常有效 - 但仅限于 Windows 7 和 8/8.1;而不是 Windows 10 - 这恰好是实现自定义 "Change Password" 凭据提供程序的噩梦。
我的做法是: a) 检查 CredentialProvider.cpp 中 SetUsageScenario() 中的 "cpus" 变量是否等于 CPUS_CHANGE_PASSWORD,如果是,则将全局布尔标志设置为 TRUE。
b) 然后,这是关键部分,在函数 SetStringValue() 中,我使用 fieldID(对于所有标准 Windows 7/8 安装和 MS 提供的默认 CP)- 像这样:
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) 然后,最后在 Get CSampleCredential::Serialization() 中,我检查了密码的复杂性,并根据需要返回了正确的 CP 值——如下:
//
// 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;
}
提供此信息是为了帮助可能需要帮助的任何人跟踪 WRAPPED 自定义凭据提供程序中的新密码字段。