10

I'm seeing some odd behaviour here using PrincipalContext.ValidateCredentials. The set-up is two Active Directory domains in parent/child setup (so we have primary domain company.com and sub-domain development.company.com).

When I validate credentials against the primary domain, ValidateCredentials behaves as expected, returning true for good user/pass pairs, and false for anything else.

However if I validate a user in the sub-domain, ValidateCredentials returns true for both good username/passwords AND invalid users. If I provide a valid user with an invalid password, it correctly returns false.

Now I'm working around it at the moment by doing UserPrincipal.FindByIdentity() first and if the user exists, then calling ValidateCredentials -- but I'd like to understand what's going on.

Another workaround I've looked at is by passing the username through as domain\username as the MSDN entry for ValidateCredentials states:

In each version of this function, the userName string can be in one of a variety of different formats. For a complete list of the acceptable types of formats, see the ADS_NAME_TYPE_ENUM documentation.

...of which this form of username is listed. But this causes ValidateCredentials to always return true, no matter what combination of username and password I pass in.

The pertinent code is:

bool authenticated = false;

// Various options tried for ContextOptions, [etc] inc. explicit username/password to bind to AD with -- no luck.
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain, null, ContextOptions.Negotiate, null, null))
{
    log(pc.ConnectedServer + " => " + pc.UserName + " => " + pc.Name + " => " + pc.Container);
    using (var user = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, username))
    {
        if (user != null)
        {
            log(user.DistinguishedName + "; " + user.DisplayName);
            authenticated = pc.ValidateCredentials(username, password);
        } else {
            log("User not found");
            // Debug only -- is FindByIdentity() needed. This should always return 
            // false, but doesn't.
            authenticated = pc.ValidateCredentials(username, password);
        }
    }
}
return authenticated;

Any and all (sensible) suggestions welcome -- I'm scratching my head over this as it just goes against all expectations.

I ought to add: this is running as myself on my machine, both of which are members of the primary domain. However I've also tried running it in a command prompt on my machine as a user of the sub-domain (runas /user:subdomain\user cmd) with exactly the same results.

Chris J
  • 30,688
  • 6
  • 69
  • 111
  • I have two domains. I can use the ip address for my domain to call validate credentials, and it succeeds if I just pass the username or password for users from either domain. I'm kind of surprised I don't have to supply this extra piece of information, and a little confused then how I'd ascertain that I'm validating the right user from the right domain. (Would it not be possible for each to have say a jsmith ?) – Greg Dec 03 '14 at 20:49

3 Answers3

20

Some amount of googling later (not that I've been in and out of google all day trying to find this anyway), I've found the answer.

Put simply, if the Guest account is enabled in the domain, ValidateCredentials will return TRUE for an unknown user. I've just checked the status of the guest user in development.company.com, and sure enough the account is enabled. If I have the guest account disabled, ValidateCredentials correctly returns false.

This is a fairly fundamental gotcha, not sure I'm keen on this behaviour... pity it's not explicitly mentioned on MSDN.

Chris J
  • 30,688
  • 6
  • 69
  • 111
  • Wow... that's an easy miss that could have been a big security hole. Thanks for the answer! – Colin Bowern Jul 24 '12 at 22:51
  • 1
    5 years past and the case is still the same. Was startled to see `principalContext.ValidateCredentials("blah", "blah", ContextOptions.Negotiate)` to return `true`. – trailmax Jul 07 '16 at 16:04
1

I have used ContextOptions.SimpleBind flag with ValidateCredentials it solved my problem..

Sample code:

    using (var context = new PrincipalContext(ContextType.Domain, "DOMAIN", null))
    {
        bool loginResult = context.ValidateCredentials(username, password, ContextOptions.SimpleBind); // returns false for unknown user
    }
Saad Shaikh
  • 175
  • 1
  • 13
0

Could it be related to this:

The ValidateCredentials method binds to the server specified in the constructor. If the username and password parameters are null, the credentials specified in the constructor are validated. If no credential were specified in the constructor, and the username and password parameters are null, this method validates the default credentials for the current principal.

TheCodeKing
  • 19,064
  • 3
  • 47
  • 70
  • No ... as it turned out, the domain-level guest account was enabled; if I disable it, ValidateCrendentials does the right thing. (And as is typical, after most of the day trying to find the answer, 20 mins after posting this I find the answer). – Chris J Sep 07 '11 at 15:39