Active Directory: Custom Password Filtering

At work, we’ve never used the “normal” way of changing Windows passwords. Historically, this is because computers were not members of the domain … so you couldn’t use Ctrl-Alt-Del to change your domain password. Now that computers are members of the domain, changing Active Directory passwords using an external method creates a lot of account lockouts. The Windows workstation is logged in using the old credentials, the password gets changed without it knowing (although you can use ctrl-alt-del, lock the workstation unlock with the new password and update the local workstation creds), and the workstation continues using the old credentials and locks the account.

This is incredibly disruptive to business, and quite a burden on the help desk … so we are going to hook the AD-initiated password changes and feed them into the Identity Management platform. Except … the password policies don’t match. But AD doesn’t know the policy on the other end … so the AD password gets changed and then the new password fails to be committed into the IDM system. And then the user gets locked out of something else because they keep trying to use their new password (and it isn’t like a user knows which directory is the back-end authentication source for a web app to use password n in AD and n-1 in DSEE).

long time ago, back when I knew some military IT folks who were migrating to Windows 2000 and needed to implement Rainbow series compliant passwords in AD – which was possible using a custom password filter. This meant a custom coded DLL that accepted or rejected the proposed password based on custom-coded rules. Never got into the code behind it – I just knew they would grab the DLL & how to register it on the domain controller.

This functionality was exactly what we needed — and Microsoft still has a provision to use a custom password filter. Now all we needed was, well, a custom password filter. The password rules prohibit the use of your user ID, your name, and a small set of words that are globally applied to all users. Microsoft’s passfilt.dll takes care of the first two — although with subtle differences from the IDM system’s rules. So my requirement became a custom password filter that prohibits passwords containing case insensitive substrings from a list of words.

I based my project on OpenPasswordFilter on GitHub — the source code prohibits exact string matches. Close, but not quite 🙂 I modified the program to check the proposed password for case insensitive substrings. I also changed the application binding to localhost from all IP address since there’s no need for the program to be accessed from outside the box. For troubleshooting purposes, I removed the requirement that the binary be run as a service and instead allowed it to be run from a command prompt or as a service.  I’m still adding some more robust error handling, but we’re ready to test! I’ve asked them to baseline changing passwords without the custom filter, using a custom filter that has the banned word list hard coded into the binary, and using a custom filter that sources its banned words list from a text file. Hopefully we’ll find there isn’t a significant increase in the time it takes a user to change their password.

My updated code is available at http://lisa.rushworth.us/OpenPasswordFilter-Edited.zip

11 comments

  1. Marcos Rodrigues Vilas Boas says:

    Hi, i’m trying to first run the original OPF and later i’ll try to do like the way is here. But i’m having an issue that the password filter only works with ctrl+alt+del. If i force a change by the server, the password filter is completely ignored. Could you know what it would be ?

    • Lisa says:

      Hmmm … Since the policy is being applied in some cases, you’ve got the password complexity requirements set properly (last step of https://docs.microsoft.com/en-us/windows/desktop/secmgmt/installing-and-registering-a-password-filter-dll). Do you have more than one domain controller? The filter needs to be installed and registered on each domain controller within the domain.

      I ran a load simulation using admin-initiated password resets (dsmod user \”$strUID\” -pwd $strPassword -mustchpwd no -s $strDomainController -u $strAdminUID -p $strAdminPassword) so can confirm that admin-initiated resets should invoke the filter.

      I had a problem with the OPF code several iterations back — the domain controller would sometimes OK a password it should have failed. If I ran the dsmod command with should-be-failed passwords against the same domain controller ten times, it would approve the password one or two times. Through a great deal of debugging (documented on this site), I isolated the problem down to the DLL failing to make a call out to the external service. In the course of my troubleshooting, I’ve added quite a bit of Windows Event logging to my newest iteration of the project on GitHub. There is a debugging level you can set on the dllmain.cpp to compile a DLL that is very verbose in its event logging. You can then tell the difference between LSA not invoking the filter with its PasswordFilter call, the custom DLL not communicating to the service, or the service returning something unexpected.

  2. solar says:

    hello lisa,
    How can I use your edited cose with the installer.
    Which path can i manually copy the files to… or can i just replace the files on the original with your edited copies.

    Thanks,
    Solar

    • Lisa says:

      The zip file contains the Visual Studio project – you can build the project or grab the version I built from http://lisa.rushworth.us/BuiltPasswordFilter.zip (md5sum b0f7ab3aba71f3b9b80273d6b86d06d0)

      There are three files needed for installation: OpenPasswordFilter.dll, OPFService.exe, and opfdict.txt (when you build the project yourself, just make this notepad and add whatever words you want to exclude from user passwords). The filter needs to be installed on ALL domain controllers in the domain. If one is missing, users can set a password on that domain controller to something that is not compliant.

      Installing:

      1. Ensure “Password must meet complexity requirements” is enabled. We set this on our domain controller group policy object – Computer Configuration\Windows Settings\Security Settings\Account Policies\Password Policy\
      2. Install .NET 3.5 feature if it is not already installed (I mounted a Windows 2012 ISO, Windows2012R2.ISO, to DVD and then specified ?:\sources\sxs as the install source)
      3. Copy OpenPasswordFilter.dll to SYSTEM32
      4. Register DLL (HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Notification Packages – append, as a new line, the string OpenPasswordFilter)
      5. Make an \OPF folder somewhere & copy OPFService.exe into that directory (my testing and documentation uses D:\OPF)
      6. Copy opfdict.txt to the same folder as the OPFService.exe
      7. Create service (sc create OpenPasswordFilter binPath= d:\opf\opfservice.exe )
      8. Reboot
      9. Start service (set to autostart once you know everything is working, until then start the service manually so it if all goes terribly awry you can just reboot.)
      10. Test (Set password to something that should fail, and it should fail. Use a sting that should work and it should work.)
  3. Chris says:

    Have you in the mean time perhaps evaluated which version is the quickest and in how far user experience is impacted?

    I’m thinking about using your code, adding MySQL capabilities and using it at work, would this be okay with you?
    Thanks and best regards
    Chris

    • Lisa says:

      Feel free to use and adapt it however you would like (the OpenSource license, GNUv2, is included with the zipped code file). I’ve used it at a Fortune 500 company (~13,000 users) for about six months now and haven’t seen any adverse impact on the DCs or user experience.
      Happy Friday!
      –Lisa

  4. Francesco says:

    Thanks a lot for your support.
    I solved the issue, was a mistake in Group Policy Configuration, now is working fine.

    Thanks
    Best Regards
    Francesco

  5. Francesco says:

    Hello,
    I very interisting article. I am trying to use your password filter but I seems that lsass doesn’t invoke passwordfilter dll.
    DLL is loaded, but if I try to change password it doesn’t call dll.

    I have a PC on my domain and trying to change password using CTRL+ALT+DEL, is it correct ?
    Could you help ?
    Thanks

    • Lisa says:

      The call is made regardless of the password change source – using ctrl-alt-del or an admin password reset should run the proposed password through the custom filter. Did you add the filter into the Notification Packages list (https://msdn.microsoft.com/en-us/library/windows/desktop/ms721766(v=vs.85).aspx)? I’ve needed to reboot each domain controller after the change is made for the DLL to be called.

      If the DLL has been registered as a notification package, you might use something like Process Monitor (https://docs.microsoft.com/en-us/sysinternals/downloads/procmon) to watch the calls. While this is not something I’d do on a production domain controller in the middle of the day, it’s a great tool to use on sandbox servers. If prod is your only choice and there are multiple domain controllers in a site, you could create a firewall rule to temporarily restrict access from devices other than your testing workstation.

Leave a Reply

Your email address will not be published. Required fields are marked *