3

The basic question

Is there a way validate an Active Directory password if it is expired, using .NET or some other kind of LDAP query? To be clear, I'm not talking about checking whether the password is expired, I'm talking about verifying that an expired password is correct.

I found this answer that explains how it can be done using the Win32 API's LogonUser() function. I've asked a separate question regarding problems with getting this to work from PowerShell. Here, I'm asking if it can be done by any means other than the Win32 API, preferably .NET.

Further consideration of the topic

The PrincipalContext.ValidateCredentials method and the DirectoryEntry constructor both return the same logon failure message whether the the password is incorrect, expired, or needs to be changed at the next logon. There's no way to distinguish between an incorrect password and a correct but expired password.

Active Directory does not expose the password hashes, and AFAIK there is no way to work around that (which is probably a good thing...), so trying to make the script hash the password and compare it to the directory is a dead end.

The best solution I've been able to come up with using .NET (or anything other than the Win32 API, for that matter), is this kludgy workaround:

  1. Check whether the password is expired (by checking whether the difference between the pwdLastSet date and the current date is greater than the maximum password age).
  2. If it is expired, check whether it is locked out. If it is, unlock it.
  3. Read the badPwdCount attribute.
  4. Attempt to authenticate by any means that increases badPwdCount if it fails (ValidateCredentials, DirectoryEntry, the runas command, many other ways). Verify that a logon error is thrown.
  5. Read badPwdCount again. If it has increased, the password provided is incorrect. If it has not increased, the password is incorrect.

(Step 2 is necessary because failed authentication attempts do not increase badPwdCount if the account is locked out.)

However, I see a few weaknesses to this plan:

  • It can produce false results if an authentication attempt from another source occurs between the "before" and "after" readings of badLogonCount, or if badLogonCount is reset by an administrator or the expiration of the lockout duration.
  • The script must ensure that the same domain controller is used throughout, to avoid false results due to convergence delays.
  • In some cases, the script must make changes (unlock the account), otherwise it won't work. Ideally, a validation operation shouldn't change any aspect of the data it examines.

Although these issues would be encountered rarely, and I can think of ways to mitigate them, those ways are pretty messy and not 100% reliable, and overall this is far from an ideal solution.

Is there any direct way to check this with .NET or any kind of LDAP query—some means of authenticating that produces error results that distinguish between invalid credentials and valid credentials for an expired password, as LogonUser() does, or some other creative way of distinguishing that's not subject to pitfalls like the idea outlined above?

Similar questions, but no answers

This topic has been raised multiple times before, but there are no answers to this specific question.

Both of the answers to this one are specific to validating the credentials when the "User must change password at next logon" flag is set. On a cursory glance, the second answer appears to address the case when the password is expired, because one of the code branches throws a "1907 Password Expired" exception, but if you read the code carefully you can see that this error message is misleading; it's only thrown in cases where the "User must change password at next logon" flag is set and the credentials are correct.

Both of these are answered with links to answers that suggest using LogonUser().

[Note: I added the c# and vb.net tags because although the question is language-independent, I believe these are the tags that are most likely to bring it to the attention of people knowledgeable in this subject; the other tags appear to have very low visibility on their own, and there were only 7 views in the first 5 hours.]

Community
  • 1
  • 1
Adi Inbar
  • 12,097
  • 13
  • 56
  • 69
  • What's wrong with the P/Invoke sample? I'm not aware of a managed way to do this aside from that. – Brian Desmond Dec 10 '14 at 00:20
  • Well, ask you can see in the liked question I posted an hour earlier, I was running into difficulties getting it to work from PowerShell. I've gotten around that issue, but in a way that makes little sense to me, serving to highlight what a messy solution this is. Given that AD does, obviously, provide errors that identify the specific reason for an authentication failure, it seems odd that only the Win32 API reports that to the user, and other authentication methods simply report success or failure. I guess it's a case of "oh, come on, there ***has*** to be a better way...!" – Adi Inbar Dec 10 '14 at 03:25

0 Answers0