2

I am writing an installer that installs SQL, beforehand a user is prompted to enter the SA username/password which will be created for them. When SQL installs it verifies this password against the Active Directory policy and will fail if it doesnt match.

What I want to do is verify the password input by the user is valid before proceeding to install SQL.

How can I validate a password is correct against Active Directory rules?

Note I do not have a login to verify as per this answer, but simply a password to verify.

I am currently trying this, but writing "password" which I know is not allowed doesnt throw an exception

try
{
    System.DirectoryServices.DirectoryEntry localMachine = new System.DirectoryServices.DirectoryEntry("WinNT://" + Environment.MachineName);
    ListPasswordPolicyInfo(Environment.MachineName);
    System.DirectoryServices.DirectoryEntry newUser = localMachine.Children.Add("localuser", "user");
    newUser.Invoke("SetPassword", new object[] { "3l!teP@$$w0RDz" });
    newUser.Invoke("SetPassword", new object[] { "password" });
    //newUser.CommitChanges();
    //Console.WriteLine(newUser.Guid.ToString());
    localMachine.Close();
    newUser.Close();
}
catch(Exception e)
{
    Console.WriteLine(e.Message);
}
Community
  • 1
  • 1
Chris
  • 26,744
  • 48
  • 193
  • 345

2 Answers2

5

After a lot of pain I have found the C# solution to this using NetValidatePasswordPolicy. Use the supporting structs off of PInvoke and the following code

public static NET_API_STATUS ValidatePassword(string password)
{
    var outputArgs = new NET_VALIDATE_OUTPUT_ARG();
    var inputArgs = new NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG();

    IntPtr inputPointer = IntPtr.Zero;
    IntPtr outputPointer = IntPtr.Zero;

    try
    {
        inputArgs.PasswordMatched = true;
        inputArgs.ClearPassword = Marshal.StringToBSTR(password);

        // If using a secure string
        ////inputArgs.ClearPassword = Marshal.SecureStringToBSTR(secureStringPassword);

        inputPointer = Marshal.AllocHGlobal(Marshal.SizeOf(inputArgs));
        Marshal.StructureToPtr(inputArgs, inputPointer, false);

        NET_API_STATUS status = NetValidatePasswordPolicy(System.Environment.MachineName, IntPtr.Zero, NET_VALIDATE_PASSWORD_TYPE.NetValidatePasswordChange, inputPointer, ref outputPointer);

        if (status == NET_API_STATUS.NERR_Success)
        {
            outputArgs = (NET_VALIDATE_OUTPUT_ARG)Marshal.PtrToStructure(outputPointer, typeof(NET_VALIDATE_OUTPUT_ARG));

            if (outputArgs.ValidationStatus == NET_API_STATUS.NERR_Success)
            {
                // Ok
            }

            return outputArgs.ValidationStatus;
        }
        else
        {
            return status;
        }
    }
    finally
    {
        if (outputPointer != IntPtr.Zero)
        {
            NetValidatePasswordPolicyFree(ref outputPointer);
        }

        if (inputArgs.ClearPassword != IntPtr.Zero)
        {
            Marshal.ZeroFreeBSTR(inputArgs.ClearPassword);
        }

        if (inputPointer != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(inputPointer);
        }
    }
}
Chris
  • 26,744
  • 48
  • 193
  • 345
  • One of the structs needed, `FILETIME`, isn't referenced on the PInvoke page, but is on a separate one: http://www.pinvoke.net/default.aspx/Structures/FILETIME.html?diff=y – nateirvin Dec 17 '14 at 22:17
  • This does work for the local server. However, it will not work for servers that are not on the same domain or where you are not an admin b/c you cannot supply a username & password to the NetValidatePasswordPolicy call. – Oleg Fridman Jan 25 '16 at 10:16
  • Also, according to https://msdn.microsoft.com/en-us/library/windows/desktop/aa370661(v=vs.85).aspx this should not be used for checking password complexity policy against AD. – Qerts Feb 16 '18 at 13:10
  • @Qerts MSDN states: "This function can also be used to verify that passwords meet the complexity, aging, minimum length, and history reuse requirements of a password policy. This function also provides the means for an application to implement an account-lockout mechanism. The NetValidatePasswordPolicy function does not validate passwords in Active Directory accounts and cannot be used for this purpose. The only policy that this function checks a password against in Active Directory accounts is the password complexity (the password strength)." So exactly what it is used for. – Daniel Fisher lennybacon Dec 31 '18 at 13:07
  • I'm sorry to add a down-vote, but this should not be used against Active Directory, at least according to Microsoft. – Jamie Oct 22 '19 at 01:03
-1
try{    
var userName = "bob";
    using (var pc = new PrincipalContext(ContextType.Domain)
    {
          var user = UserPrincipal.FindByIdentity(pc, userName);
          user.ChangePassword(oldpassword, newpassword); //Checks password policy
          //or
          user.SetPassword(newpassword); //Not positive checks password policy but I believe it 2.
    }
}
catch(PasswordException ex)
{
//do something with ex
}
Tony
  • 1,297
  • 10
  • 17
  • Will this work even if "bob" doesnt exist? (P.s. nice use of bob, we use bobs throughout code :D ) – Chris Jan 28 '14 at 16:31
  • I just checked and this doesnt work if Bob doesnt exist.. in this case how am I meant to check password policy? – Chris Jan 28 '14 at 16:35
  • Honestly, I don't know. We have a class that checks password policy before it gets to this point. Using Regex and our own set of rules so that we can return them quick for Ajax checkmark things on a web front end. I doub't it because you have to Find the UserPrincipal. You could setup a "dummy" account which is disabled to check however. I don't know of another way, there might be however. – Tony Jan 28 '14 at 16:35