Reputation: 878
My objective is simple: pre-validate a new password client-side (Javascript) as an initial check the password matches the domain/ou password policy, to avoid wasting server resources rejecting bad passwords and give faster responses to users.
The question: How can I get user password policy from Active Directory?
I especially need to know the password "format", password length, capital and special characters requirements, etc. The final validation will, of course, be Active Directory itself. But first I want to use Javascript as a performance optimization, and I'm pretty sure I can manage the Javascript if I can just retrieve the password format requirements for a specific user/OU on the C#/ASP.Net end.
Currently, I'm stuck trying to find WHAT the current password policy is for the user. Yes, user Alice
might use the password domain policy, but Bob
could have a different password policy in his OU.
This website will be installed in an institution with thousands of users; we want to minimize the back and forth validation against Active Directory. Additionally, having this in Javascript can eventually help in compliance with NIST Special Publication 800-63, which among other things asks for prompt feedback to users on relative password strength. For now, I must be able to make the code work on Windows 2008, 2008 R2 and 2012.
I'm currently able to change the password in C#, and I can get the error, but it's it's all or nothing, and not helpful for client-side validation.
public static PasswordChangeResultsDTO ChangeUserPassword(PasswordChangeRequestDTO request)
{
try
{
bool isPasswordChanged = false;
SearchResult result = LdapHelper.GetUser(request.Username, request.OldPassword);
if (result != null)
{
using (DirectoryEntry userEntry = result.GetDirectoryEntry())
{
userEntry.Invoke("ChangePassword", new object[] {request.OldPassword, request.NewPassword});
userEntry.CommitChanges();
isPasswordChanged = true;
}
}
return new PasswordChangeResultsDTO {PasswordChanged = isPasswordChanged};
}
catch (COMException comException)
{
LoggingHelper.Instance.WriteException(comException);
string message = comException.ErrorCode == -2147022651
? "The password does not meet the password policy requirements"
: comException.Message;
return new PasswordChangeResultsDTO {PasswordChanged = false, Message = message};
}
catch (TargetInvocationException targetInvocationException)
{
LoggingHelper.Instance.WriteException(targetInvocationException);
string message;
if (targetInvocationException.InnerException != null)
{
var comException = targetInvocationException.InnerException as COMException;
if (comException != null)
{
message = comException.ErrorCode == -2147022651
? "The password does not meet the password policy requirements"
: comException.Message;
}
else
{
message = targetInvocationException.InnerException.Message;
}
}
else
{
message = targetInvocationException.Message;
}
return new PasswordChangeResultsDTO {PasswordChanged = false, Message = message};
}
catch (Exception ex)
{
string msgError = (null != ex.InnerException) ? ex.InnerException.Message : ex.Message;
string msgSource = (null != ex.InnerException) ? ex.InnerException.Source : ex.Source;
string msgStackTrace = (null != ex.InnerException) ? ex.InnerException.StackTrace : ex.StackTrace;
string msgOutput = String.Format(CultureInfo.InvariantCulture,
"Exception in {3} MSG[{0}] SOURCE[{1}] STACK[{2}]",
msgError, msgSource, msgStackTrace, MethodBase.GetCurrentMethod().Name);
LoggingHelper.Instance.Fatal(msgOutput);
throw;
}
}
Upvotes: 2
Views: 812
Reputation: 41008
Finding out this information at the domain level is easy. Figuring out if any Group Policies have overridden the default is hard.
At the domain level, there are attributes at the domain itself that govern the default password policy for the domain. You can bind to the domain itself (i.e. LDAP://domain.com
) and read these attributes:
minPwdLength
: The minimum character lengthpwdHistoryLength
: The number of old passwords that can't be reused.pwdProperties
: This is a bit flag that could mean various things, which you can read about under the "PasswordProperties" section here. It's likely to be set to 1 (DOMAIN_PASSWORD_COMPLEX
), which means a password must include at least two of either uppercase, lowercase and numbers.If you want to go through the effort to read group policies that would apply to the user's OU, there doesn't seem to be any .NET libraries to do that. You have to resort to using unmanaged code. There is an example here that uses the IGPMDomain
interface from C#, but you will have to adapt it to find the GPO for the right OU.
Upvotes: 1