Checking Password Complexity

Michael put some sample code into WSC2 that showed people how to check passwords using the NetValidatePasswordPolicy API. It's a very flexible API, and it's meant to handle situations where an app maintains its own password database, like SQL Server. However, you can use it to check whether a password for some other use complies with domain policy – here's a code snippet:

 

#include <windows.h>

#include <lm.h>

#include <stdio.h>

#pragma comment(lib, "Netapi32.lib")

 

int __cdecl wmain(int argc, wchar_t* argv[])

{

      NET_API_STATUS stat;

      NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG InputArg = {0};

      NET_VALIDATE_OUTPUT_ARG* pOutputArg = NULL;

      wchar_t* wzServer = L"\\\\YOUR_DC_HERE";

      wchar_t* wzPwd = L"NiceLongComplexPwd!";

 

      InputArg.ClearPassword = wzPwd;

      InputArg.PasswordMatch = TRUE;

     
 

      stat = NetValidatePasswordPolicy(wzServer, NULL, NetValidatePasswordChange, &InputArg, (void**)&pOutputArg);

      printf("stat = %d, ValidationStatus = %d\n", stat, pOutputArg->ValidationStatus);

 

      NetValidatePasswordPolicyFree((void**)&pOutputArg);

 

      return 0;

}

 

There's a difference between this and the code sample in our book – we used NetValidatePasswordReset, and this uses NetValidatePasswordChange, which will force the DC to check any custom password filters that might be installed. Scott Field considers our previous code to be buggy in this respect, and this code fixes the problem. I'd also like to thank Umit Akkus for helping sort out exactly how to get this working – the fact you have to set the PasswordMatch member wasn't obvious from my attempt to RTFM. The answer you want will be contained in pOutputArg->ValidationStatus, assuming that stat == ERROR_SUCCESS.

The SDK documentation isn't quite accurate – it claims you need to be running this on a server OS, and it turns out to work just fine on Vista, but it is true that the API requires Win2k3 or later. There are some things to think about – only if you remote this to a domain controller will it use the domain password policy and the domain's password filter (if one is installed). If you make the wzServer argument NULL, it will run against the local system, using the local policy which may or may not be different than what you use for the domain. I've also been told that DsGetDcName is the right API to use these days – I know a bunch of different ways to find domain controllers, but some of them are outdated – this is the API that should work best now.

Hopefully, this will help someone out there – trying to do this just by reading MSDN wasn't going too well for me. There's actually a funny story behind this – I knew what the right API was, but not how to use it, and then couldn't get it working. I then asked about it on an internal mailing list, and Scott steered me in the right direction. A day or so later, someone helpfully pointed out that there was sample code to do this in my own book (dum-de-dum!), and in my defense, it's been a long time (2002) and Michael wrote the sample. Scott then even more helpfully pointed out that Michael wasn't doing it quite right, so this should set the record straight.