返回

Windows自定义凭据提供程序:解决“无效的验证信息类”错误

windows

Windows 网络登录自定义凭据提供程序 (PLAP) 开发与 "无效的验证信息类" 错误解决

我正在尝试创建一个自定义的凭据提供程序,类似于 Microsoft 的 PLAP (rasplap.dll),用来增强 Windows 登录的网络身份验证。我需要添加一个额外的输入字段(如 OTP/TOTP)来实现多因素认证 (MFA),而微软提供的 PLAP 只支持用户名和密码,在连接 VPN 后进行基于域的身份验证。

我的 dll 文件里有个 getSerialization 方法,负责在提供用户名和密码后执行登录操作。但问题是,我遇到了一个错误消息:“无效的验证信息类 (The validation information class requested was invalid)”。即使调试时所有消息都正常输出,在 Windows 登录界面尝试登录时,仍然会弹出这个错误。下面我将详细分析这个问题,并提供多种解决方案。

问题原因分析

这个 "无效的验证信息类" 错误通常与 LogonUser 函数及其相关安全上下文有关。虽然你的代码表面上调用流程正确,但Windows 登录流程对安全设置的要求很高,问题可能出在以下几个方面:

  1. 权限不足: LogonUser 函数根据 dwLogonType 参数的不同, 需要不同的权限。例如,LOGON32_LOGON_NETWORK 可能由于当前用户的权限不足, 导致失败。

  2. 安全策略限制: 本地安全策略或组策略可能限制了某些登录类型或身份验证方式, 导致 LogonUser 即使参数正确也无法成功。

  3. LogonUser 函数参数错误: 即使一个参数微小错误都可能导致失败. LogonUser 对用户名, 域, 以及密码格式可能有细微要求。

  4. NTLM 协商问题 : 你使用了 RetrieveNegotiateAuthPackageNew 函数检索 NTLM 验证包,可能 NTLM 在当前环境中被禁用或配置不正确, 会出现这个错误.

  5. 凭据提供程序 (Credential Provider) 的注册或配置问题: 如果凭据提供程序本身注册不完整, 或者与 Windows 安全子系统交互有误,即使 LogonUser 成功, 也可能在后续流程中产生错误。

  6. VPN连接状态: 虽然代码尝试用 rasdial建立 VPN, 但VPN 的建立需要时间. 在 VPN 尚未完全就绪前, 调用 LogonUser 进行域身份验证可能失败.

解决方案

针对以上可能的原因, 我们逐一提供排查方法和对应的解决措施:

1. 检查并调整权限

  • 原理: 确保运行凭据提供程序代码的用户具有足够的权限来执行网络登录。
  • 操作步骤:
    • 如果是本地调试,尝试使用管理员权限运行 Visual Studio 或调试器。
    • 如果凭据提供程序已部署,确保安装和注册凭据提供程序的服务或进程以具有足够权限的用户身份运行(例如,Local System 账户)。
  • 代码示例:(无) 这是系统配置层面的问题, 不需要代码修改。
  • 进阶技巧 如果是在企业域环境中测试,要特别注意域策略和组策略的配置,看是否有影响本地登录的设置.

2. 审查安全策略

  • 原理: 确保本地安全策略或组策略允许网络登录和 NTLM 身份验证。

  • 操作步骤:

    • 打开本地安全策略编辑器(secpol.msc)或组策略编辑器(gpedit.msc)。
    • 导航到“本地策略”->“安全选项”。
    • 检查以下策略设置:
      • “网络安全:LAN 管理器身份验证级别”: 确保允许 NTLMv2 或 NTLM(如果环境需要)。
      • "网络访问:限制允许对命名管道和共享进行的匿名访问"。确保未阻止 LogonUser
      • 其他与网络登录和远程访问相关的策略。
  • 代码示例:(无) 这是系统配置层面的修改。

  • 安全建议: 调整策略设置时请遵循最小权限原则,仅允许必要的访问和身份验证级别。

3. 仔细核对 LogonUser 参数

  • 原理: 确保传递给 LogonUser 的用户名、域名和密码的格式正确无误,包括大小写、特殊字符等。
  • 操作步骤:
    • 使用绝对确定的用户名、域名和密码进行测试, 排除变量导致的问题。例如使用一个明确的本地账户,且密码简单。
    • 仔细检查是否有字符串截断,或者编码导致的问题。
    • 可以尝试不同的 dwLogonType 值. 例如先改为 LOGON32_LOGON_INTERACTIVE 进行测试。
  • 代码改进 (示例) :
//在调用LogonUser前打印参数,进行核对
   wchar_t logBuffer[512];
   swprintf_s(logBuffer, sizeof(logBuffer) / sizeof(wchar_t), L"LogonUser Parameters: User: %s, Domain: %s, LogonType: %lu", _rgFieldStrings[SFI_EDIT_TEXT], L"vpncloud.com", dwAuthFlags);
   LogMessage(logBuffer);

if (!LogonUser(_rgFieldStrings[SFI_EDIT_TEXT], L"vpncloud.com", _rgFieldStrings[SFI_PASSWORD], LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hToken))
    {
    // ... 错误处理
}

4. NTLM 协商问题的进一步检查

  • 原理 : 有时 Windows 会自动进行 Kerberos 认证. 但由于你的场景先拨号 VPN, 然后登录, 可能 Kerberos 无法顺利使用. 需要明确指定 NTLM.

  • 操作步骤:

    • 确保你的网络环境中启用了 NTLM 身份验证.
    • 尝试显式指定 NTLM 提供程序: 将 LOGON32_PROVIDER_DEFAULT 改为 LOGON32_PROVIDER_WINNT50LOGON32_PROVIDER_WINNT40, 具体用哪个取决于你的目标系统版本。
    • 查看 Windows 事件日志(Event Viewer), 在 "Security" 日志中筛选与 "Audit Failure" 相关的事件, 分析是否有详细的 NTLM 错误信息.
  • 代码示例(无): 这是 LogonUser 函数参数的调整,无需单独的代码片段。

5. 确认凭据提供程序的正确注册

  • 原理: 凭据提供程序必须正确注册到系统中才能被 Windows 识别和使用。
  • 操作步骤:
    • 检查你的凭据提供程序 DLL 是否已使用 regsvr32 工具正确注册。
    • 查看注册表 (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers) 中是否存在你的凭据提供程序的 CLSID。
    • 验证 ICredentialProviderICredentialProviderCredential 接口实现正确。
  • 代码和命令示例
    regsvr32.exe  YourCredentialProvider.dll   # 注册命令. 确保用管理员运行
    

6. VPN 连接同步问题

  • 原理: rasdial 建立 VPN 连接可能需要时间,如果在 VPN 完全建立前就进行身份验证,会导致失败.

  • 操作步骤:

    • 增加等待时间. WaitForSingleObject(pi.hProcess, INFINITE); 表示你等待 rasdial 进程结束。 但结束不代表 连接完全可用! 可以在 WaitForSingleObject 再加入 Sleep(5000); (等待5秒), 观察效果.
    • 更可靠的做法: 查询 VPN 连接状态。 可以使用 Windows 的 RasGetConnectionStatus API.
    • 改进Rasdial逻辑. 你当前只是简单的等待rasdial结束. 可以增加检查机制。 比如rasdial成功后的返回码是0. 你需要检查 dwExitCode.
  • 代码改进(示例):

    
    WaitForSingleObject(pi.hProcess, INFINITE);
    
    DWORD dwExitCode;
    if (!GetExitCodeProcess(pi.hProcess, &dwExitCode) || dwExitCode != 0) {
         //  VPN 连接可能有问题
         //  ....  可以从dwExitCode进一步分析.
    }
    
    Sleep(5000); // 先简单粗暴等待5秒。
    //  或者 使用RasGetConnectionStatus
    //   RASCONNSTATUS status;
    //   status.dwSize = sizeof(RASCONNSTATUS);
    //   DWORD dwRet = RasGetConnectionStatus(..., &status); //  你需要填充参数
    //  if (dwRet == 0 && status.rasconnstate == RASCS_Connected){... 
    //     VPN连接就绪!
    //    }
    
    // ... 后面代码.
    
    

进阶调试方法

  • WinDbg 调试:
    如果上述步骤仍然无法解决,使用 WinDbg 或 Visual Studio 附加到 lsass.exe (Local Security Authority Subsystem Service) 进程, 可以更深入的分析问题所在. 设置与登录和凭据提供程序相关的断点.
  • ProcMon 排查
    使用Process Monitor工具, 可以监视你的凭据提供程序, 以及 lsass.exe 的文件, 注册表访问情况。 有助于分析是否有权限, 路径错误.

一些额外提醒

  • 仔细检查用于构建凭据提供程序的 Visual Studio 项目设置。确保项目配置(例如,字符集、链接器设置)正确无误。
  • 在开发阶段. 把 Credential Provider 的错误日志级别设为最高. 尽量输出更详尽的错误和调试信息, 以便于定位。

我强烈建议按以上顺序逐步排查,结合代码修改和系统配置调整,最终应该能找到问题的根源。希望以上分析对你有帮助。