如何验证域凭据(来自本机代码)?

Ian*_*oyd 7 security ntlm kerberos networkcredentials

我想针对域控制器验证一组凭据.例如:

Username: joel
Password: splotchy
Domain:   STACKOVERFLOW
Run Code Online (Sandbox Code Playgroud)

在.NET 3.5及更高版本中,您可以使用PrincipalContext.ValidateCredentials(username, password).

否则你就麻烦了.

按照Microsoft知识库文章如何验证Microsoft操作系统上的用户凭据中的代码,我到达您调用的位置AcceptSecurityContext:

ss = AcceptSecurityContext(
      @pAS._hcred,           //[in]CredHandle structure
      phContext,             //[in,out]CtxtHandle structure
      @InBuffDesc,           //[in]SecBufferDesc structure 
      0,                     //[in]context requirement flags
      SECURITY_NATIVE_DREP,  //[in]target data representation
      @pAS._hctxt,           //[in,out]CtxtHandle strcture
      @OutBuffDesc,          //[in,out]SecBufferDesc structure
      ContextAttributes,     //[out]Context attribute flags
      @Lifetime);            //[out]Timestamp struture
Run Code Online (Sandbox Code Playgroud)

除了函数失败:

SEC_E_NO_AUTHENTICATING_AUTHORITY (0x80090311)

功能失败了.无法联系任何权限进行身份验证.这可能是由于以下条件:

  • 身份验证方的域名不正确.
  • 该域名不可用.
  • 信任关系失败了.

这将是一个有用的错误,除了我可以使用以下方法从.NET 3.5验证相同的凭据:

using (PrincipalContext context = new PrincipalContext(ContextType.Domain, domain))
{
    valid = context.ValidateCredentials(username, password);                
}
Run Code Online (Sandbox Code Playgroud)

可能发生什么允许.NET验证一组凭据,而本机代码不能?


更新:LogonUser也失败了:

LogonUser("joel@stackoverflow.com", null, "splotchy", 
      LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_WINNT50, out token);
Run Code Online (Sandbox Code Playgroud)

1311 - There are currently no logon servers available to service the logon request
Run Code Online (Sandbox Code Playgroud)

更新二:我已尝试过首选Negotiate提供程序,以及Windows NT4旧版"NTLM"提供程序

String package = "Negotiate"; //"NTLM"

QuerySecurityPackageInfo(package, [out] packageInfo);
...
AcquireCredentialsHandle(
      null,                 //[in] principle
      package,              //[in] package
      SECPKG_CRED_OUTBOUND, //[in] credential use
      null,                 //[in] LogonID
      pAuthIdentity,        //[in] authData
      null,                 //[in] GetKeyFn, not used and should be null
      null,                 //[in] GetKeyArgument, not used and should be null
      credHandle,           //[out] CredHandle structure
      expires);             //[out] expiration TimeStamp structure
Run Code Online (Sandbox Code Playgroud)

Har*_*wok 1

我认为这是为了解决与您发布的另一个问题相同的问题。

我有点明白你现在想做什么。让我回顾一下您在另一篇文章中写的内容。

Username  Password  Domain             Machine on domain?  Validate as
========  ========  =================  ==================  ============== 
iboyd     pass1     .                  No                  Local account 
iboyd     pass1     (empty)            No                  Local account
iboyd     pass1     stackoverflow.com  No                  Domain account
iboyd     pass1     .                  Yes                 Local account
iboyd     pass1     (empty)            Yes                 Domain account
iboyd     pass1     stackoverflow.com  Yes                 Domain account
Run Code Online (Sandbox Code Playgroud)

你想要

  1. 对来自您的计算机不信任的域的用户进行身份验证
  2. 对来自您的计算机信任的域的用户进行身份验证
  3. 验证本地用户

您可以通过与域控制器进行正确的 SSPI 握手来实现前两种情况。您在另一个问题中提到的知识库文章正在执行环回 SSPI 握手。在第一种情况下它不起作用,因为客户端计算机不信任您正在验证的域。这应该就是你看到的原因SEC_E_NO_AUTHENTICATING_AUTHORITY

简而言之,如果您想做与

PrincipalContext.ValidateCredentials(username, password);
Run Code Online (Sandbox Code Playgroud)

您需要以不同于域用户的方式处理本地用户。对于域用户,您需要调用ldap_bind_s使用给定的凭据绑定到域控制器。对于本地用户,您需要使用ADsOpenObject使用给定的凭据绑定到WinnT://YourComputerName 。这就是PrincipalContext.ValidateCredentials我在 Reflector 中读到的内容。

我不认为有任何等效的单一原生 API 可以为您做同样的事情。