Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate domain credentials (from native code)?

i want to validate a set of credentials against the domain controller. e.g.:

Username: joel
Password: splotchy
Domain:   STACKOVERFLOW

In .NET 3.5 and newer you can use PrincipalContext.ValidateCredentials(username, password).

Otherwise you're in trouble.

Following the code in the Microsoft Knowledge Base article How to validate user credentials on Microsoft operating systems, i get to the point where you call 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

except that the function fails with:

SEC_E_NO_AUTHENTICATING_AUTHORITY (0x80090311)

The function failed. No authority could be contacted for authentication. This could be due to the following conditions:

  • The domain name of the authenticating party is incorrect.
  • The domain is unavailable.
  • The trust relationship has failed.

This would be a useful error, except that i can validate the same credentials from .NET 3.5 using:

using (PrincipalContext context = new PrincipalContext(ContextType.Domain, domain))
{
    valid = context.ValidateCredentials(username, password);                
}

What could be happening that allows .NET to validate a set of credentials, while native code cannot?


Update: LogonUser also fails:

LogonUser("[email protected]", null, "splotchy", 
      LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_WINNT50, out token);

with

1311 - There are currently no logon servers available to service the logon request

Update Two: i've tried both the preferred Negotiate provider, as well as the Windows NT4 legacy "NTLM" provider

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
like image 211
Ian Boyd Avatar asked Mar 15 '12 16:03

Ian Boyd


1 Answers

I presume that this is to solve the same problem as another question that you posted.

I kind of understand what you are trying to do now. Let me recap what you wrote on another post.

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

You want to

  1. Authenticate a user from a domain that your machine doesn't trust
  2. Authenticate a user from a domain that your machine trusted
  3. Authenticate a local user

You can achieve the first two cases by doing proper SSPI handshaking with the domain controller. The KB article that you are referring to in another question is doing loop back SSPI handshaking. It's not going to work in case number one because the client machine does not trust the domain that you are authenticating to. That should be why you are seeing SEC_E_NO_AUTHENTICATING_AUTHORITY.

To cut it short, if you want to do exactly the same thing as

PrincipalContext.ValidateCredentials(username, password);

you need to handle the local user differently from the domain user. For domain user, you need to call ldap_bind_s to bind to the domain controller using the given credentials. For local user, you need to use ADsOpenObject to bind to the WinnT://YourComputerName using the given credentials. This is what PrincipalContext.ValidateCredentials doing from what I read in the Reflector.

I don't see there is any equivalent one single native API doing the same thing for you.

like image 91
Harvey Kwok Avatar answered Nov 06 '22 16:11

Harvey Kwok