Menu

Passwod policy violation and Red Hat Directory Server

2017-09-15
2017-09-15
  • Michael Blaha

    Michael Blaha - 2017-09-15

    Hello,
    please, help me, as I am unable to find a way to detect a descriptive reason for password change denial.

    Using the following code:

    PasswordModifyExtendedRequest passwordModifyExtendedRequest = new PasswordModifyExtendedRequest(userDN, currPassword, newPassword, new Control[]{new Control("1.3.6.1.4.1.42.2.27.8.5.1")});
    passwordModifyExtendedRequest.setResponseTimeoutMillis(1000);
    LDAPConnection ldapConnection = ldapManager.getLdapConnection();
    PasswordModifyExtendedResult extendedResult = (PasswordModifyExtendedResult) ldapConnection.processExtendedOperation(passwordModifyExtendedRequest);
    System.out.println(extendedResult);
    

    and Red Hat Directory Server, I am getting this response:

    PasswordModifyExtendedResult(resultCode=19 (constraint violation), messageID=2, diagnosticMessage='Failed to update password', responseControls={PasswordPolicyResponseControl(errorType='insufficient password quality', isCritical=false)})
    

    When I try using Apache Directory Studio (connecting to the same Red Hat Directory Server), I get the following response, which is much more descriptive:

    [LDAP: error code 19 - invalid password syntax - password must be at least 8 characters long]
    

    Is there a way to get the information using UnboundID LDAP SDK as well?

    Thank you for any response

     
  • Michael Blaha

    Michael Blaha - 2017-09-15

    UPDATE

    I've managed to get the message using a regular ModifyRequest like that:

    ModifyRequest modifyRequest = new ModifyRequest(
                "dn: " + user.getDn(),
                "changetype: modify",
                "replace: userPassword",
                "userPassword:: " + newPassword
                );
    System.out.println(modifyRequest);
    LDAPResult modifyResult = ldapManager.getLdapConnectionAsAdmin().modify(modifyRequest);
    System.out.println(modifyResult);
    

    with a result, which is just perfect:

    LDAPException(resultCode=19 (constraint violation), errorMessage='invalid password syntax - password must be at least 8 characters long', diagnosticMessage='invalid password syntax - password must be at least 8 characters long', ldapSDKVersion=4.0.1, revision='26090')
    

    However, I'm adding the password value as a plaintext. Is there a proper way to encode it using the UnboundID LDAP SDK? Or is that just fine?

    Thanks!

     
  • Michael Blaha

    Michael Blaha - 2017-09-15

    UPDATE v2

    Okay, so the code is as follow:

    ...
    import com.unboundid.util.Base64;
    ...
    ModifyRequest modifyRequest = new ModifyRequest(
                "dn: " + user.getDn(),
                "changetype: modify",
                "replace: userPassword",
                "userPassword:: " + Base64.encode(newPassword);
                );
    LDAPResult modifyResult = ldapManager.getLdapConnectionAsAdmin().modify(modifyRequest);
    

    And now it works just as expected...

    Last question: what is the purpose of PasswordModifyExtendedRequest then? Why does it not return the detailed message with the concrete reason?

     

    Last edit: Michael Blaha 2017-09-15
  • Neil Wilson

    Neil Wilson - 2017-09-15

    It sounds like you’ve mostly been able to work around the problem on your own while I’ve been typing my (admittedly, very long-winded) response to your original request, but let me try to respond to that original request and your updates all at once.

    It should definitely be possible to get the desired result using the UnboundID LDAP SDK. We just need to figure out what the difference is between the request that you’re sending with the LDAP SDK and the one generated by Apache Directory Studio.

    The first thing that I would try would be to issue the LDAP SDK request without the password policy request control. Even though the response control has an error type field that can give you a general-purpose reason for the failure (and in this case you’re getting the “insufficient password quality” reason), it doesn’t include a field that lets you include additional information about the nature of the failure. The diagnostic message in the extended result could be used for that purpose, but in this case, it’s just got a generic message.

    The fact that you get the desired message when you try to change the password with a regular LDAP modify request without the password policy request control instead of a password modify extended request with the password policy request control suggests one of a couple of things:

    • The presence of the password policy request control changes the nature of the server’s response so that the diagnostic message actually includes less useful information. It should be pretty simple to verify this by sending the same password modify request request without the password policy request control.
    • The server uses a different set of code for processing the password modify extended operation than it does when processing a password change with a regular LDAP modify operation, and the code for processing the extended operation simply doesn’t include the same helpful information about why the password isn’t acceptable when generating the response.

    My guess is that it’s probably the second one, but it’s really a question of whether you know that Apache Directory Studio is actually using a password modify extended operation or just a regular LDAP modify. I’m not really familiar with Apache Directory Studio, so I can’t say for sure whether it supports the extended operation and if so under what conditions it might try to use it.

    If we really want to nail it down, then we could capture and analyze the LDAP traffic that passes between the client and the server and see what’s different when using the LDAP SDK versus when using Apache Directory Studio. You could use a protocol analyzer like Wireshark to do this, but the LDAP SDK actually comes with an ldap-debugger tool that is specifically designed for this purpose. It operates as a very simple LDAP proxy server that decodes everything that passes through it and prints it out in a somewhat user-friendly way. Instead of connecting directly to the Red Hat directory server, you would point the ldap-debugger tool at the Red Hat server, and then you would point your client (either the LDAP SDK code or Apache Directory Studio) at the ldap-debugger tool. For example:

    $ tools/ldap-debugger --hostname redhat.server.address --port 389 --listenPort 1389
    

    Then, capture the output (you can add an “--outputFile {path}” argument to send it to a file if you want), and post it here. The output that the ldap-debugger tool produces for extended operations isn’t necessarily as useful as for other types of LDAP operations (because an extended operation can be used to request just about anything), and the same is true of controls, but I’ll be able to look at the output and decode it for you to see what the difference is.

    If you do end up wanting to go with an LDAP modify operation, I would recommend that you generate the modify request more via code instead of by generating LDIF. That is, instead of:

    ModifyRequest modifyRequest = new ModifyRequest(
                "dn: " + user.getDn(),
                "changetype: modify",
                "replace: userPassword",
                "userPassword:: " + Base64.encode(newPassword.getBytes());
    

    You should use something like:

    Modification passwordReplacementModification = new Modification(
                ModificationType.REPLACE, "userPassword",
                newPassword.getBytes());
    ModifyRequest modifyRequest = new ModifyRequest(user.getDn(),
                passwordReplacementModification);
    

    Not only will this be more efficient (because the LDAP SDK doesn’t have to parse the LDIF), but it’s also safer because the LDAP SDK will make sure that any necessary escaping is handled for you. That isn’t quite as important in this case as in other cases (for example, it’s vital that you generate LDAP search filters programmatically rather than constructing them from their string representations so that you don’t end up with an LDAP injection vulnerability; see https://nawilson.com/2017/03/22/understanding-and-defending-against-ldap-injection-attacks/ for more details about that), but it’s still a good idea.

    And for your last question about the purpose of the LDAP password modify operation, it’s an attempt at creating a standardized way to change a password in an LDAP directory server. Most servers do allow you to change a password using a regular LDAP modify operation (and in some servers, that’s the only way to do it), but the password modify extended operation does offer a few potential benefits that are harder to get with a regular LDAP modify operation:

    • With an LDAP modify operation, you have to know what attribute is used to store the password. A lot of the time, it’s userPassword, but there are alternatives. For example, RFC 3112 defines the authPassword attribute type that can be used to hold encoded passwords in a specified format, since the original intention for userPassword was for it to hold the unencoded clear-text representation of the password (but most servers ignore that and use an encoded representation that actually protects the password). And Active Directory uses the unicodePwd attribute with a whole different set of constraints https://nawilson.com/2010/08/26/ldap-password-changes-in-active-directory/. The password modify extended operation is supposed to help you cut through the red tape and just let the directory server figure out everything for you. You tell it whose password to change, optionally tell it what the user’s current password is, and optionally tell it what new password to use, and the server should take care of all of the implementation-specific details.
    • A password modify operation allows you to provide the user’s current password. This is beneficial for a couple of reasons. First, it potentially allows you to perform a password change over an otherwise unauthenticated connection, so it can be more efficient than if you had to bind first. But the second benefit is that it allows you to verify that the user actually does know their current password. It’s harder to do this in a regular LDAP modify request. You can do it with a bind beforehand, but that won’t always work if the account is in a special state (like the password is expired so they can’t authenticate, but a password modify will still let them change the password). Or you may be able to delete the old password in the modify request, but I’m not sure if all servers support that.
    • A password modify operation potentially allows you to omit the new password. In some servers, this allows the server to generate a new password for the user and include it in the password modify response. This is typically used for an administrative reset when it’s just a temporary password that is given to the user so that they can authenticate to set a new password of their own choosing, and some servers might not support this, but it is a potentially useful feature nonetheless.

    But ultimately, a lot of servers probably do use different code for handling a password modify operation than for a password change using a regular LDAP modify operation, so you might see subtle differences in behavior between the two. Most of the time, these differences are unintentional, so if that’s the case here, then you could raise it with the Red Hat developers and maybe they’ll fix the problem so that the server returns the same type of response in both cases.

     
  • Michael Blaha

    Michael Blaha - 2017-09-18

    Wow, thank you for all the answers, explanations and hints!

    I've only added the password policy request control as one of many attempts to obtain more information from the server. Without it, the response is pretty much the same, just without the control response:

    PasswordModifyExtendedResult(resultCode=19 (constraint violation), messageID=2, diagnosticMessage='Failed to update password')
    

    So your guess was most probably correct.

    I've investigated Apache Directory Studio code further and found out it uses ModifyRequest to change the password (as far as I can tell - there might be something I missed in the code).

    Sadly, I have not been able to connect via the ldap-debugger tool due to some security issues, tried to use all the variants of useSSL, listenUsingSSL, trustAll, useStartTLS, most of the combinations result in handshake failure. Just to complete the info, the following code works to initiate a connection (I'm not a security expert):

        //...
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, new TrustManager[]{new TrustAllCertsMgr()}, new java.security.SecureRandom());
        SocketFactory socketFactory = sslContext.getSocketFactory();
        FailoverServerSet failoverServerSet = new FailoverServerSet(new String[]{host}, new int[]{port}, socketFactory);
        LDAPConnectionPool pool = new LDAPConnectionPool(failoverServerSet, new SimpleBindRequest(dn, password), 0, 1);
        //...
        return pool.getConnection();
    //...
    private class TrustAllCertsMgr implements X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    
        public void checkClientTrusted(X509Certificate[] certs, String authType) {
        }
    
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
        }
    }
    

    But actually I don't know what more could be extracted from the log.

    As a temporary solution, I think I'll continue using the PasswordModifyExtendedRequest and call the regular ModifyRequest in case of failure due to constraint violation in order to get a more detailed message.

    Once again thank you for all your help! I greatly appreciate it.

     
  • Neil Wilson

    Neil Wilson - 2017-09-18

    From the information you’ve provided, I think there’s a pretty good chance that the Red Hat server just doesn’t provide the requested information when you’re using the password modify extended operation, so you might have to fall back to using a regular LDAP modify. You might want to report this to them to see if they'll consider updating their server to provide the same useful information in the response to a password modify operation as in a regular LDAP modify response (or maybe they can tell you how to get it if there already is a way).

    There’s nothing wrong with changing the password with an LDAP modify operation as long as the client knows all of the necessary details, like which attribute to modify and whether there are any special requirements for interacting with that password. I haven’t used the Red Hat server since well before it was the Red Hat server, but it’s probably safe to assume that you can just perform a modify of the userPassword attribute to set the user’s password.

    If you’re trying to use TLS with the ldap-debugger tool, then you need to make sure that you run it with the appropriate arguments needed to listen for secure connections, and that the tool has access to a certificate keystore. You can create a keystore with a self-signed certificate with a pair of commands like:

    $ keytool -genkeypair \
         -alias server-cert \
         -dname CN=server.example.com,O=Example Corporation,C=US \
         -ext SAN=DNS:server.example.com \
         -validity 365 \
         -keyalg RSA \
         -keysize 2048 \
         -sigalg SHA256withRSA \
         -keystore {keystore-filename} \
         -storetype JKS \
         -storepass {keystore-password} \
         -keypass {keystore-password}
    
    $ keytool -selfcert \
         -alias server-cert \
         -dname CN=server.example.com,O=Example Corporation,C=US \
         -ext SAN=DNS:server.example.com \
         -validity 365 \
         -sigalg SHA256withRSA \
         -keystore {keystore-filename} \
         -storetype JKS \
         -storepass {keystore-password} \
         -keypass {keystore-password}
    

    Then, you can invoke the ldap-debugger tool with arguments like:

    $ tools/ldap-debugger --hostname {ldap-server-address} \
         --port {ldap-server-ssl-port} \
         --useSSL \
         --trustAll \
         --listenPort {ldap-debugger-listen-port} \
         --listenUsingSSL \
         --keyStorePath {keystore-filename} \
         --keystorePassword {keystore-password}
    

    And it should be able to accept secure connections. My guess is that you probably didn’t provide it with a keystore, so it didn’t have a certificate that it could use for TLS negotiation with clients. And note that with a self-signed certificate, you’ll need to make sure that either the client is explicitly configured to trust that certificate, that it’s configured to blindly trust all certificates, or that it will prompt you about whether to trust any certificate that it doesn’t recognize.

    And by the way, you might want to check out the com.unboundid.util.ssl.SSLUtil class (and other classes in that package) for a fairly user-friendly API for using secure communication with TLS. It’s got a TrustAllTrustManager class that works basically like your private class, but there are other options for interacting with key and trust stores, and it automatically tries to select the best TLS protocols and cipher suites. What you’ve got will work if you just want to blindly trust any certificate that the server might present, but you really don’t want to do that in a production environment because it leaves the door open for a man-in-the-middle attack.

     
  • Michael Blaha

    Michael Blaha - 2017-09-19

    I've managed to get the debugger working, thanks!
    Also, I've deleted a lot of extra code and used com.unboundid.util.ssl.SSLUtil instead.

    Thank you very much, you've been a great help!

     

Log in to post a comment.