Password Reset Enforcing Directory Policies with UnboundID

末鹿安然 提交于 2019-12-23 03:47:23

问题


I'm developing a web app that let users reset their own passwords in Active Directory. I've been doing it by binding as an administrator and it works fine, but the directory policies (reuse history, characters, etc) are not being enforced. I can't bind as a user because I don't have the current password.

I read about the LDAP_SERVER_POLICY_HINTS control introduced in Windows 2008 R2 SP1 for doing that in Active Directory and even found someone who made it using Spring LDAP

Since I'm using UnboundID and there is no standard control shipped for that, I figured that I had to create my own control class. The documented OID is 1.2.840.113556.1.4.2239 and the value {48, 3, 2, 1, 1}

public class PolicyHintsControl extends Control {

    private static final long serialVersionUID = 1L;

    public final static String LDAP_SERVER_POLICY_HINTS_OID = "1.2.840.113556.1.4.2066";

    public final static byte[] LDAP_SERVER_POLICY_HINTS_DATA = { 48,
            (byte) 132, 0, 0, 0, 3, 2, 1, 1 };

    public PolicyHintsControl() {
        super(LDAP_SERVER_POLICY_HINTS_OID, false, new ASN1OctetString(
                LDAP_SERVER_POLICY_HINTS_DATA));
    }

    @Override
    public String getControlName() {
        return "LDAP Server Policy Hints Control";
    }

    @Override
    public void toString(StringBuilder buffer) {
        buffer.append("LDAPServerPolicyHints(isCritical=");
        buffer.append(isCritical());
        buffer.append(')');
    }
}

So I added this new control in the modify request like this:

public static void main(String[] args) throws Exception {

    final String host = "ldap.example.com";
    final int port = 636;
    String adminDn = "admin@example.com";
    String adminPassword = "passwd";
    String userDn = "CN=user,ou=people,dc=example,dc=com";
    String userPassword = "passwd";
    String keystoreFile = "/path/to/keystore.jks";
    String keystorePassword = "passwd";

    String passwordAttribute = "unicodePwd";

    //Password change requires SSL
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(new FileInputStream(keystoreFile), keystorePassword.toCharArray());
    TrustManagerFactory factory = TrustManagerFactory.getInstance("x509");
    factory.init(keyStore);
    final SSLUtil sslUtil = new SSLUtil(factory.getTrustManagers());
    SSLSocketFactory socketFactory = sslUtil.createSSLSocketFactory();
    Debug.setEnabled(true);

    // Connect as the configured administrator
    LDAPConnection ldapConnection = new LDAPConnection(socketFactory, host,
            port, adminDn, adminPassword);
    // Set password in AD format
    final String newQuotedPassword = "\"" + userPassword + "\"";
    final byte[] newPasswordBytes = newQuotedPassword.getBytes("UTF-16LE");
    String encryptedNewPwd = new String(newPasswordBytes);
    //Build modifications array and request
    final ArrayList<Modification> modifications = new ArrayList<Modification>();
    modifications.add(new Modification(ModificationType.REPLACE,
            passwordAttribute, encryptedNewPwd));
    ModifyRequest modifyRequest = new ModifyRequest(userDn, modifications);
    //Add the policy hints control
    modifyRequest.addControl(new PolicyHintsControl());
    //Modify already
    ldapConnection.modify(modifyRequest);
    ldapConnection.close();
}

I get the following exception:

Exception in thread "main" LDAPException(resultCode=53 (unwilling to perform), errorMessage='0000052D: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0
        ', diagnosticMessage='0000052D: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0
        ')

After researching a bit more I found that there was another update in Windows 2012 for the same control which changed the OID to 1.2.840.113556.1.4.2066 and deprecated the old OID.

Since this app can be configured with any version of AD I'd like to handle gracefully every scenario (Windows 2012, Windows 2008 R2 SP1, others). My questions are:

  1. Does anyone have successfully done this with UnboundID?
  2. Is there anyway to know if the controls are available before the modification request?
  3. What would be the best way to handle different OID's for different versions of AD for the same control? Same class or different classes?

回答1:


I'm not all that familiar with Microsoft-specific controls so I can't provide a lot of help there, but it looks like you're already on the right track with that. In this case, it actually looks like the control is working as expected and the server is rejecting the password because it's not strong enough.

Active Directory is really awful with how hard it makes it to figure things like this out, but the secret lies in the "0000052D" given in the diagnostic message. That is a reference to Active Directory system error code 0x52D, which is decimal 1325. System error codes are documented at http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx, and in this case you need to follow the "System Error Codes (1300-1699)" link (http://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx) and find the description for value 1325. The text for that error code says "Unable to update the password. The value provided for the new password does not meet the length, complexity, or history requirements of the domain." Since the point of the control you're trying to use seems to be to cause the server to perform quality checking on the new password, it looks like it's working as expected. If you use a stronger password (e.g., make it longer, include uppercase/numeric/symbol characters, etc.) then perhaps the server will accept it.

With regard to your question about figuring out what controls the server supports, the way to do that is to retrieve the server root DSE and look at the OIDs reported in the supportedControls attribute. The UnboundID LDAP SDK for Java makes this pretty easy because you can use the LDAPConnection.getRootDSE method to retrieve the root DSE, and then the RootDSE.supportsControl method to determine whether the server supports the specified control.

With regard to your question about whether to handle different OIDs with the same class or different classes, that's more a matter of style than anything else. If the control with the newer OID also uses a different encoding for the value, then that would definitely suggest making a separate class. If the value encoding is the same for both OIDs, then it's probably a matter of personal preference but even if you make them separate classes then it would be good to keep the majority of the code common rather than having the same code in two different places.



来源:https://stackoverflow.com/questions/21138911/password-reset-enforcing-directory-policies-with-unboundid

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!