I'm writing a C# program that will enforce password complexity in accordance with the Windows Group Policy setting "Password must meet complexity requirements". Specifically, if that policy is set to Enabled either on the local machine (if it's not part of a domain) or by the Domain Security Policy (for domain members), then my software needs to enforce a complex password for its own internal security.
The issue is that I can't figure out how to read that GPO setting. Google searches have indicated that I can read GPO settings with one of these two APIs: the System.DirectoryServices library in .NET Framework, and Windows Management Instrumentation (WMI), but I haven't had any success so far.
Any insights would be helpful.
There doesn't appear to be a documented API for this task, managed or otherwise.
I tried the managed route using the System.Management assembly:
ConnectionOptions options = new ConnectionOptions();
ManagementScope scope = new ManagementScope(@"\\.\root\RSOP\Computer", options);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, new ObjectQuery("SELECT * FROM RSOP_SecuritySettingBoolean"));
foreach(ManagementObject o in searcher.Get())
{
Console.WriteLine("Key Name: {0}", o["KeyName"]);
Console.WriteLine("Precedence: {0}", o["Precedence"]);
Console.WriteLine("Setting: {0}", o["Setting"]);
}
This however will not return results. It doesn't appear to be a permission issue as providing a username/password pair to ConnectionOptions
results in an exception telling you that you can not specify a username when connecting locally.
I looked at NetUserModalsGet. While this will return some information on password settings:
typedef struct _USER_MODALS_INFO_0 {
DWORD usrmod0_min_passwd_len;
DWORD usrmod0_max_passwd_age;
DWORD usrmod0_min_passwd_age;
DWORD usrmod0_force_logoff;
DWORD usrmod0_password_hist_len;
} USER_MODALS_INFO_0, *PUSER_MODALS_INFO_0, *LPUSER_MODALS_INFO_0;
..it will not let tell if the Password Complexity policy is enabled.
So I resorted to parsing secedit.exe output.
public static bool PasswordComplexityPolicy()
{
var tempFile = Path.GetTempFileName();
Process p = new Process();
p.StartInfo.FileName = Environment.ExpandEnvironmentVariables(@"%SystemRoot%\system32\secedit.exe");
p.StartInfo.Arguments = String.Format(@"/export /cfg ""{0}"" /quiet", tempFile);
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.Start();
p.WaitForExit();
var file = IniFile.Load(tempFile);
IniSection systemAccess = null;
var passwordComplexityString = "";
var passwordComplexity = 0;
return file.Sections.TryGetValue("System Access", out systemAccess)
&& systemAccess.TryGetValue("PasswordComplexity", out passwordComplexityString)
&& Int32.TryParse(passwordComplexityString, out passwordComplexity)
&& passwordComplexity == 1;
}
Full code here: http://gist.github.com/421802