Password Policy Recommendations for the Ping Identity Directory Server

When using an LDAP directory server to authenticate users, the vast majority of those authentications will make use of a password. Even though the Ping Identity Directory Server supports multiple options for two-factor authentication, you’re still likely to use a password as one of those factors. As such, ensuring that you have a good password policy in place is an essential element of your server’s security configuration.

Obviously, the biggest risk when using password-based authentication is that someone will provide the correct credentials for an account that isn’t theirs, and will be able to do whatever the account owner can do. Some of the most common ways that this can happen are:

  • The account has a really simple, easily guessable password. Maybe they use the word “password” or some other common value. Maybe their password is the same as their username or email address. Maybe their password is the same as your application or service.
  • The account owner used the same username and password across multiple sites, and one of those other sites got breached. When this happens, attackers often try to use the breached credentials to log into other sites.
  • An attacker managed to get access to the encoded representation of a user’s password and was able to crack it through some means.

Some of these are things that are out of your control as a website owner. For example, no matter how careful you are with your own site, you can’t prevent some other site from getting breached and its credentials exposed. However, you can take steps to ensure that your site is better protected against these kinds of things. In this post, I’ll discuss options for creating a password policy that will help keep your site as safe as possible.

While some of the recommendations I provide here are specific to the Ping Identity Directory Server, there is a lot of generic advice in here as well, and that may be applicable to other types of directory servers.

Require Secure Communication

Most LDAP authentication schemes that involve a password send that password to the server in the clear, without any kind of encoding or transformation. There are some that do try to obscure the password (e.g., salted challenge-response authentication mechanisms), but they are typically garbage that actually dramatically weaken the security of your environment and should be avoided at all costs. All of the best password-based authentication mechanisms send the password in the clear, which means that you must ensure that all of the communication happens over a secure, encrypted connection.

The server offers a few options to help ensure that this is done. They are as follows, in order of strongest protection to weakest protection:

  1. Disable any connection handlers that allow insecure communication. If you only allow LDAPS and not unencrypted LDAP, then there is no chance that someone with the ability to observe the communication will be able to decipher it.
  2. Set the “reject-insecure-requests” property to “true” in the global configuration. While this will allow clients to establish insecure connections to the server, it will reject most requests issued over those connections until the client has used the StartTLS extended operation to convert that connection from an insecure one to one that is secure. However, this option isn’t as good as simply disabling all insecure communication because even though it ensures that clients won’t be permitted to authenticate with an unencrypted bind request, it can’t prevent the client from sending that request in the first place.
  3. Set the “require-secure-authentication” and “require-secure-password-changes” properties to “true” in the password policy configuration. These properties ensure that the server will reject any requests that attempt to authenticate or change passwords that are sent over an unencrypted connection. Like option #2 above, this won’t prevent the requests from being sent, but will merely reject them if they are. But it also won’t protect other sensitive information that may be transferred over the connection.

Use a Strong Password Storage Scheme

When the Directory Server receives a password to store in a user entry, it encodes that password with a password storage scheme. This helps protect that password so that anyone who gains access to the entry (whether over LDAP, in a backup of the database, in an LDIF export, or in some other form) won’t be able to determine what the clear-text password really is. When the server receives a bind request that contains a clear-text password, it will use the password storage scheme and information contained in the encoded password to determine whether the provided clear-text password matches the one used to create the encoded password.

The server offers a number of different password storage scheme options, that fall into different categories:

  • Some of them, like those that use the UNIX crypt algorithm or versions of the MD5 or SHA-1 digest algorithms, are only intended for legacy purposes like migrating already-encoded passwords from another data store. You definitely shouldn’t use these for new passwords unless you absolutely have to maintain backward compatibility with a legacy system for some period of time.
  • Some of them, like those that use AES or triple-DES, use reversible encryption to encode the passwords in a way that allows the server to obtain their original clear-text value. These should not be used unless you absolutely have to support a legacy authentication scheme (like CRAM-MD5 or DIGEST-MD5) that requires that the server be able to determine the clear-text representation of the password.
  • Some of them offer salted variants of more modern digest algorithms, like 256-bit, 384-bit, and 512-bit SHA-2 variants. These schemes are acceptable, especially if you have configured an appropriate set of password validators that require users to have strong passwords, but they are very fast, which means that an attacker can make a lot of guesses in a short period of time.
  • Some of them use algorithms that are intentionally designed to require a lot of CPU processing or memory access so that they take longer to compute, or so that it’s harder to compute a lot of them at the same time. We currently offer support for the PBKDF2, bcrypt, and scrypt schemes, and plan to add others in the future. These are the strongest options available, and they will definitely have a dramatic impact on how long it will take an attacker to brute-force a password, but they can also dramatically impact the performance of legitimate authentication attempts, and also the performance of an LDIF import that includes clear-text passwords that need to be encoded.

Ideally, you should encode passwords with the strongest scheme that you can tolerate based on your load. If you don’t need more than a few hundred authentications or password changes per second, then you should probably consider the “expensive” schemes to make it as difficult as possible for attackers that may get access to encoded passwords. But if your performance demands are such that you can’t afford enough servers to handle the authentication load with these expensive schemes, then at least use one of the strong modern digests.

Note that if you choose a particular scheme now, or if you have existing passwords encoded with legacy schemes, you aren’t stuck with them, and you don’t have to require all of your users to change their passwords to transition to stronger encodings. For that, we offer the deprecated-password-storage-scheme property in the password policy configuration. If a user has a password encoded with a deprecated scheme, the server will automatically re-encode it using the default scheme the next time they use that password to authenticate.

Require Strong Passwords

It’s important to ensure that passwords are encoded in a secure manner, but it’s even more important that the passwords are strong to start with. If a user has a password that can be easily guessed, then it won’t take long to crack it using even the strongest encoding.

The best practices for choosing secure passwords have changed over the last several years. Things that people used to think were good ideas have turned out to be not so hot. Some of the things that you shouldn’t do are:

  • Don’t require users to change their passwords on a regular basis for no reason. That’s just annoying to your users and doesn’t improve security in any meaningful way. In fact, it’s more likely to cause people to make bad decisions, like just keeping the same basic password but adding a counter to the end. You should only require users to change their passwords if you suspect that they may have been compromised, or if they’ve been reset by an administrator (e.g., because the user forgot what their previous password was).
  • Don’t impose a ridiculous upper limit on the length of a password. There’s just no good reason for it. All of the good password storage schemes that we provide generate the same encoded password length regardless of the size of the password being encoded, so you’re not saving any space by preventing long passwords. With all other things being equal, longer passwords are stronger than shorter ones, so you want to encourage people to choose long passwords. It is true that some algorithms may take longer to encode a really long password, so maybe don’t let people choose passwords longer than a few hundred characters, but there’s never a good reason to impose an upper limit of something like ten or twenty characters.
  • Don’t require passwords to have different classes of characters. It’s entirely possible to have a very strong password that is comprised entirely of lowercase letters, just as it’s entirely possible for a password containing a mix of lowercase and uppercase letters, digits, and symbols to be very weak. You certainly shouldn’t prevent people from using a mix of character types, but you also shouldn’t require it for no good reason.
  • Don’t impose ridiculously low limits on the number of times the same character may appear consecutively in a password. Long, randomly generated passwords are very strong, and yet it’s entirely possible that such passwords may have the same character appear two or three or four times in a row. It’s very frustrating to choose what is clearly a very strong password only to have it rejected for some stupid reason like this. If you want to prevent passwords comprised of repeated characters, like “aaaaaaaaaa”, then it would probably be better to require them to have a minimum number of unique characters than limiting the number of times the same character may appear in a row.

So what should you do? Despite being a government document, NIST Special Publication 800-63B has some really good advice. And here are some good guidelines that include these and other recommendations:

  • Don’t allow users to choose obvious or weak passwords. This includes commonly used passwords, dictionary words, and words related to the service that you’re operating. To help with this, the Ping Identity Directory Server offers a dictionary password validator that can reject attempts to use passwords in a given dictionary file, and we offer a couple of instances of that validator preconfigured with dictionary files to use with it: one with over 500,000 of the most commonly used passwords, and one with over 400,000 English words. We recommend that you also configure a validator with your own custom dictionary that includes at least words related to your organization and the products or services you provide., and you may also want to use dictionaries of non-English words. Also, we’ve got plans to provide additional options in this area in the near future.
  • Impose a minimum length for passwords (but not a maximum). If a password is too short, then it’s too easy to brute force. There are a couple of ways that you can do this in the Ping Identity Directory Server:

    • You can use the length-based validator to simply impose a minimum length. NIST 800-63B suggests at least eight characters, but to be honest, even a complex eight-character password can be cheaply brute-forced in a handful of hours.
    • Another option is to use the haystack validator, which uses the concept of password haystacks to evaluate a password’s complexity as a factor of both its length and the types of characters it contains. This is perhaps more complicated to explain to users, but it can do a better job of ensuring resistance to brute force attacks.
  • Prevent users from choosing passwords that are related to other information that you have about them. For example, you should not allow them to choose a password that matches their username, email address, telephone number, date of birth, etc. The Ping Identity Directory Server offers an attribute value password validator that can be used to ensure that passwords aren’t allowed to match (or optionally contain) the values of other attributes in the entry.
  • When users are changing their passwords, they should not be permitted to choose a new password that is too similar to their current password. For example, if their current password is “ThisIsMyStrongPassword1”, it’s probably a good idea to prevent them from choosing “ThisIsMyStrongPassword2” as their new password. To achieve this, the Ping Identity Directory Server offers a similarity validator that uses the Levenshtein distance algorithm to compare a proposed new password with the user’s current password (which would have to be provided as part of the password change, since we don’t recommend configuring the server to store passwords in a reversible form) to determine the minimum number of changes (characters added, removed, or replaced).
  • As noted above, it’s not a good idea to reject passwords just because they have the same password repeated a few times in a row, but it might not be a bad idea to require that they have a minimum number of different characters. The Ping Identity Directory Server offers a unique characters password validator that can accomplish this.
  • Let the user know what the requirements are. The Ping Identity Directory Server offers a get password quality requirements extended operation that you can use to programmatically retrieve information about the validation that the server will perform, and it offers a password validation details control that you can use to determine which requirements were satisfied and which were not when trying to set a password. I wrote an earlier blog post about using these features.
  • Recommend the use of a password manager, and recommend using them to generate long random passwords. Certainly don’t do anything that would discourage their use, like prevent users from pasting text into fields on login forms, or interfere with things that might legitimately happen in long random passwords (like having the same character repeated a few times).

If your service imposes additional requirements on password quality that can’t be enforced with the above validators, then I would first recommend taking a hard look at whether they are actually good requirements. Lots of organizations have really dumb rules that either weaken security or at least don’t do anything to improve it while also annoying the end users. But if it turns out that you do have a legitimate need to impose additional types of requirements, then our Server SDK provides an API that allows you to create your own custom password validator implementations.

Require the Current Password for Password Changes

When a user wants to change their password, it’s a good idea to verify that it’s actually them and not someone who has gotten access to their account (e.g., someone using a shared computer that was left logged in). Further, if the user provides their current password when choosing a new password, it allows the server to perform additional types of validation for the new password that may not otherwise be available (e.g., making sure that the new password is not too similar to the current password).

The Ping Identity Directory Server offers a password policy setting to enforce this. If the password-change-requires-current-password property is set to true, then any user changing their own password will be required to provide their current password. This applies to both password changes that use a standard LDAP modify operation and those that use the password modify extended operation. The password modify extended operation already includes a field for the current password, so meeting this requirement for that type of operation is obvious. It’s less obvious for a standard LDAP modify operation, but the way to achieve that is to provide the clear-text representation of the current password in a delete modification, and the desired new password in an add modification, like:

dn: uid=test.user,ou=People,dc=example,dc=com
changetype: modify
delete: userPassword
userPassword: oldPassword
add: userPassword
userPassword: newPassword

Note that this does not apply to administrative password resets in which one user changes the password for another user.

Enable a Password History

Password reuse is one of the most common ways for accounts to get breached. The biggest risk is when someone uses the same password across multiple sites: if one of those sites is breached and the passwords are obtained, then attackers can try the same credentials on other sites. However, it’s also a bad idea for a user to repeatedly use the same password on the same site.

Repeatedly using the same password on the same site is most commonly an issue if that site requires people to change their password, which, as already stated, is an ill-advised strategy. If you don’t enable password expiration, then people will be less likely to arbitrarily change their passwords, and therefore will be less likely to want to reuse one that they’ve chosen before.

Another common reason for someone wanting to reuse the same password is if they have accidentally locked their account by mis-typing their current password too many times. If this happens, then it’s almost certainly the result of a bad account lockout configuration, which is something that I’ll cover below.

Even if the most common reasons that a user would want to reuse a password are the result of ill-advised configurations, it’s still a good idea to prevent someone from repeating a password that they’ve already had in the past. The server already prevents a user from choosing a new password that is the same as their current password, but if you want to prevent users from repeating passwords they’ve had in the past, then the best way to do that is by enabling a password history. The password policy configuration offers a couple of properties to do this:

  • password-history-count — Specifies the maximum number of passwords to retain in the history, regardless of how long it’s been since the passwords were in use.
  • password-history-duration — Specifies the length of time to retain previous passwords in the history, regardless of how many of them have been used during that time.

In either case, the server will maintain an operational attribute with the encoded representations of the user’s former passwords, along with a timestamp indicating when the password was added to the history.

Note that if you do enable a password history, then you may also want to prevent users from changing their passwords too frequently, which you can do with the min-password-age property. This is especially true if you have configured a maximum history count because it’s a common trick for a user who really wants to reuse a previous password to change their password multiple times in quick succession until the password they want to reuse has been flushed from the history. But even though this trick is less effective if you configure a maximum duration rather than a maximum count, it’s still a good idea to prevent frequent password changes in that case too, just to avoid unnecessarily building up a large history.

A relatively short minimum password age, like a day or perhaps even an hour, should be enough of a deterrent to someone who wants to just blow out the password history so that they can reuse a password. Note, however, that the minimum password age only applies to self-changes and not administrative resets. If a user chooses a new password and then immediately forgets it so that they cannot authenticate, then an administrator will be able to reset the password even within the minimum age. And if force-change-on-reset is true, indicating that users are required to choose a new password after an administrative reset, then the minimum age will not interfere with that, either.

Don’t Allow Pre-Encoded Passwords

As previously noted, the server does not store passwords in the clear, but instead encodes all clear-text passwords that it is asked to store (ideally in a strong, non-reversible form). While it is technically possible to send the password to the server in a form that is already encoded, this is generally a very bad idea, for many reasons. Some of them include:

  • If a password is pre-encoded, the server can’t determine its clear-text representation, and therefore can’t verify that it meets the configured password quality requirements.
  • Because there are multiple ways of encoding a single password (e.g., using a different salt), there is no way to ensure that the new password isn’t just an alternate encoding for the user’s current password, or for another password in the user’s password history.
  • The Directory Server generally supports a wide range of applications. If applications pre-encode passwords before sending them to the server, then it makes it more difficult to migrate to a stronger encoding if that becomes necessary or desirable.

The Ping Identity Directory Server offers an allow-pre-encoded-passwords configuration property that controls whether it will accept pre-encoded passwords, and it has a value of false by default. We strongly recommend leaving this as the default.

We do understand that there may be legitimate cases in which it may be necessary to send pre-encoded passwords to the server. For example, if you’re synchronizing data between two sources, and if the data being synchronized includes pre-encoded passwords, then you might not have access to the clear-text password. In that case, rather than reconfiguring the password policy to allow any client to supply passwords in a pre-encoded form, a better alternative is to use the password update behavior request control in any such applications. One of the features that this control offers is a way to indicate that a pre-encoded password should be accepted on a per-change basis. The Ping Identity Synchronization Server (which is included at no additional charge with the Ping Identity Directory Server) makes use of this control for the synchronization that it performs.

Note that the value of the allow-pre-encoded-passwords property does not have any effect on LDIF import. The server will always permit pre-encoded passwords contained in an LDIF file that is loaded into the server with the import-ldif command or with the LDIF import task.

Don’t Allow Multiple Passwords

The two most common attribute types for storing passwords, userPassword (defined in RFC 4519) and authPassword (defined in RFC 3112), are both permitted to have multiple values. In theory, this means that a user could have multiple passwords. However, this is not a good idea. If a user has multiple passwords, then there is no way to control which one is used for any given authentication attempt, so all of them will be considered valid and represents the potential opportunity for the account to be breached.

Further, password policy state is not tied specifically to any password, but rather is maintained on a per-account basis. This means that if a user is allowed to have multiple passwords, then they may be able to exploit that to circumvent certain password policy functionality. For example, if password expiration is enabled (which I’ve already mentioned is a bad idea), then a user with two passwords could just keep changing one of them and continue using the other indefinitely. The same would apply to similar restrictions, like being forced to choose a new password after an administrative reset.

The Ping Identity Directory Server provides an allow-multiple-password-values configuration property to control this behavior, and it has a value of false by default. We do not recommend changing its value to true.

Configure Failure Lockout

If an attacker can get access to the encoded representation of a password, they can throw a lot of hardware at trying to crack it. But if they can’t get access to the encoded representation (and hopefully, that’s a very difficult thing to do), they can still try to crack a password by simply repeatedly trying to log in to an application that authenticates against the server. This is much slower, but it can still be quite effective for users with weak passwords.

You can make this a lot harder by limiting the number of login attempts that someone can make before the account is locked. If an account is locked, then it doesn’t matter what password is tried because any attempt will be rejected. The Ping Identity Directory Server offers support for two types of failure lockout:

  • Permanent lockout, in which you don’t specify a maximum lockout duration. In this case, the account lockout will remain in effect until an administrator resets the user’s password.
  • Temporary lockout, in which you do specify a maximum lockout duration (via the lockout-duration configuration property). In this case, the lockout will automatically be lifted after a period of time, but it can still be unlocked earlier by a password reset.

I would strongly recommend using the temporary lockout with a relatively short lockout duration (e.g., one minute). This is sufficient to severely limit the rate at which an attacker can make guesses, but it also ensures that the lockout is short enough that an administrator shouldn’t need to get involved in the case that a user accidentally locks their account but still remembers the correct password. However, I would also strongly recommend setting the lockout failure count (via the lockout-failure-count property) to be high enough that it’s very unlikely to be reached accidentally by merely fat-fingering the password. Ten failed attempts seems like a good value for this purpose.

Note that the Ping Identity Directory Server offers an additional useful feature for helping to prevent accidental failure lockout. If the ignore-duplicate-password-failures property is set to its default value of true, the server will consider repeated attempts to use the same wrong password as just a single failed attempt. This can be helpful in cases where the password has been configured in an application, and that application hasn’t been updated after the user changes their password. If you’re concerned about this scenario, then you may also want to consider enabling password retirement, as described in a later section.

Configure Password Reset Constraints

The account’s owner should be the only one to know its password. If they forget their password and they need to have it reset, there are two basic options:

  • Have the password reset by a human administrator. In this case, the administrator can choose the password themselves, or they can allow the server to generate it for them (more on this later). Either way, the administrator will end up knowing the password, which means that it should only be usable for the purpose of allowing the user to set a password of their own choosing. The force-change-on-reset configuration property can be used to enable this, and you may also want to set a value for the max-password-reset-age property to limit how long that temporary password is valid.
  • Create an automated process that allows the user to reset their own password after providing reasonable proof of their identity. To help with this, the Ping Identity Directory Server offers a feature that we call password reset tokens. A password reset token is a single-use password that can be delivered to the user through some out-of-band mechanism (e.g., email message, text message, voice call, mobile push notification, etc.) and can be provided to the password modify extended operation to allow the user to choose a new password. See the DeliverPasswordResetTokenExtendedRequest class in the LDAP SDK for more information about this feature.

Use a Strong Password Generator

The server has the ability to generate passwords under certain circumstances. Some of them include:

  • When using the password modify extended operation. If the new password field of the request is left empty, the server can generate the new password and return it in the extended response. The password generator used for this purpose is specified using the password-generator property in the password policy configuration.
  • When using the deliver password reset token extended request, as described in the previous section. The password generator used for this purpose is specified using the password-generator property in the extended operation handler configuration.

We may also introduce additional methods for generating passwords in the future.

When generating a one-time password, password reset token, or single-use token, the time frame for the generated password is inherently very limited (typically a matter of minutes). In such cases, it may be acceptable to have the password be relatively simple. For example, something like eight alphanumeric characters should be sufficient, and you may even feel comfortable with something simpler.

When using the password modify extended operation to perform an administrative reset, the time that a generated password may be valid can be limited by the max-password-reset-age property in the password policy configuration, but there is no such limit for a generated password used for a self-change. In these cases, you may want to ensure that the generated password is substantially stronger. By default, the Ping Identity Directory Server will generate a passphrase that is at least 20 characters long and consists of at least four randomly chosen words. This generated password will hopefully be something that is both memorable and easy to type, and will also serve as an example of the kind of strong passwords that you hope users choose for themselves (although it’s still a better idea to use a password manager in most cases, and to let it generate a strong random password).

Note that the password generator is not guaranteed to generate passwords that will satisfy the configured set of password validators, but the server will accept and use the generated password even if it would have been rejected if the user had chosen it for themselves. If you have eccentric password quality requirements, you may wish to customize the password generator to be more likely to create something that the server would accept.

Consider Using Password Retirement

The Ping Identity Directory Server offers a useful feature called password retirement that allows a user to change their password, but continue using their former password as an alternative to their new password for a limited period of time. This is helpful for cases in which the password might be used in multiple places (especially if it’s configured in an application, and even more especially if there might be multiple instances of that application), because it gives the user the opportunity to update all of the things that use the password without them failing immediately as soon as it is changed. I wrote an earlier blog post about password retirement, so I won’t repeat it here, but it’s definitely something to consider, at least for application accounts.

Consider Using Account Status Notifications

Another potentially useful password policy feature that the Ping Identity Directory Server offers is account status notifications. This allows the server to perform custom processing whenever certain events occur that affect user accounts, like a user’s account has been locked as a result of too many failed attempts, an authentication attempt has failed because the account is expired or has been unused for too long, a user’s password is changed or reset by an administrator, etc.

The server includes support for a couple of different types of account status notification handlers out of the box: one that is capable of sending an email message to the end user and/or to administrators when such an event occurs, and one that simply logs a message about the event. Our Server SDK also provides support for creating custom account status notification handlers, so you can integrate with other systems that you might use in your environment. We’re also going to be substantially improving our account status notification handler subsystem in the near future.

One purpose of account status notification handlers is to provide a means of letting end users know about substantial events that affect their accounts. This is useful from a security perspective because if a user sees something that indicates something may be amiss, then they can inquire about it. But account status notification handlers can also serve as a kind of auditing mechanism for these kinds of events so that administrators are made aware of things that might suggest a problem or an attack. For example, if a large number of accounts are being locked out, or if the same account is repeatedly being locked out, then that suggests someone might be trying a password guessing attack.

Alternatives to Arbitrary Password Expiration

I’ve already pointed out on multiple occasions that arbitrary password expiration is not recommended. However, there may be reasons that you want users to change their passwords that might not be simply the result of them having the same password for too long.

For example, if you believe that one or more user accounts might have had their passwords exposed (whether in the clear or in encoded form), then it’s a very good idea to require them to choose a new password. The Ping Identity Directory Server offers a couple of features to help with this:

  • The password policy offers a require-change-by-time configuration property that allows you to require all users associated with that policy to change their passwords by a specified time. Failure to comply will cause their account to be locked until the password is reset by an administrator.
  • The password policy state extended operation provides a mechanism for manipulating the account state for a user or set of users. Using this extended operation, or the manage-account tool that provides a simple command-line interface to the operation, you could put a specified account or set of accounts in a “must change password” state that will require them to change their passwords the next time they authenticate. See my previous blog post about managing password policy state for more information.

If you’re considering password expiration as a means of ensuring that users will be required to authenticate every so often or their accounts will be locked, then we offer a better alternative to that: idle account lockout. If you update the password policy to enable last login time tracking (via the last-login-time-attribute and last-login-time-format configuration properties), then the server will maintain a record of the last time that each user successfully authenticated. If you also set a value for the idle-lockout-interval property, then a user who hasn’t authenticated in at least that length of time will be locked out of their account until the password is reset by an administrator.

If the reason for forcing a password change is because you want to upgrade the password storage scheme that you’re using, you can do that without actually requiring a password change. As noted above in the section on password storage schemes, you can simply make the desired scheme the new default and mark the old scheme as deprecated. The next time a user authenticates with a password encoded with the deprecated scheme, it will be automatically re-encoded using the new default scheme.

If you have other reasons that you were considering password expiration that aren’t covered by these options, then let us know what they are, and we’ll let you know whether there might be a suitable alternative.

Other Related Settings Outside the Password Policy

There are a few other related settings that you might want to consider setting, even though they’re not strictly part of the password policy configuration. They include:

  • You should enable data encryption in the server so that data is not stored in the clear in the database. The best way to do this is to enable encryption when setting up the server, but if you need to do it after the fact, you can use the encryption-settings tool to manage the set of encryption settings definitions that are available in the server, then update the values of the the encrypt-data, encryption-settings-cipher-stream-provider, encrypt-backups-by-default, and encrypt-ldif-exports-by-default global configuration properties. Enabling encryption can be done on the fly, and any subsequent writes will cause the target entry to be encrypted, but you may still want to export the data to LDIF and re-import to ensure that all the existing data gets encrypted.
  • You may wish to configure the server to delay the response to a failed bind operation by a specified length of time (e.g., maybe a second). This can help slow down online password guessing attacks, and it can be used either in conjunction with or as an alternative to locking an account after too many failed attempts. This setting can be configured through the failed-bind-response-delay property in the LDAP connection handler configuration.
  • You may want to configure the userPassword and authPassword attributes as sensitive attributes within the server, and ensure that they can never be retrieved over LDAP, even in encoded form, and even by root users. Check out my earlier blog post about sensitive attributes for more information on this subject.
  • Consider enabling one or more two-factor authentication mechanisms. While it’s still important to ensure that users have strong passwords, two-factor authentication adds yet another layer of protection that an attacker must overcome even if they do happen to learn a user’s password. I also wrote an earlier blog post about this topic.

Sensitive Attributes in the Ping Identity Directory Server

The Ping Identity Directory Server offers a rich access control framework that can be used to help ensure that clients are only given the level of access that they need in the server. It’s a deny-by-default mechanism that means that a user doesn’t get access to something unless there’s a rule that grants it, and our default access control configuration is very restrictive. However, there may be cases in which access control might not be good enough.

For example, let’s say that you want to absolutely ensure that clients cannot retrieve encoded passwords from user entries. To help with this, the server does offer the following rule defined in the out-of-the-box set of global ACIs:

(targetattr="userPassword || authPassword")
(version 3.0;
acl "Prevent clients from retrieving passwords from the server";
deny (read,search,compare)

ACIs that deny access take precedence over those that allow it, so the above rule will ensure that no client that is subject to access control evaluation will be able to retrieve either the userPassword or authPassword attribute, even if another rule explicitly or implicitly tries to grant that access.

However, there is one significant flaw with the above access control rule: it only applies to clients that are subject to access control enforcement. If an authenticated user has the bypass-acl or bypass-read-acl privilege, then the searches they request won’t be subject to access control evaluation, and therefore the entries returned in response to those searches won’t be pared down by the access control handler.

To address this limitation, the server does offer additional forms of protection that can apply even to clients that aren’t subject to access control restrictions. For example, client connection policies can be used to indicate which types of operations are allowed. And sensitive attributes allow you to impose restrictions around access to specified attributes even for privileged users.

A sensitive attribute configuration definition includes the following properties:

  • attribute-type — The names or OIDs of the attribute types to which the sensitive attribute definition applies. At least one attribute type must be specified in each sensitive attribute definition.
  • include-default-sensitive-operational-attributes — Indicates whether the sensitive attribute definition should also apply to certain operational attributes that might include values for the target attribute. At present, this includes the ds-sync-hist attribute, which may hold current or former values for attributes in the entry for the purposes of replication conflict resolution.
  • allow-in-returned-entries — Indicates whether the specified attribute types are allowed to be included in entries that are returned to the client. The value may be one of “allow” (in which the attribute is allowed to be returned, as long as nothing else prevents it), “suppress” (in which the attribute will never be returned, even if something else permits it), or “secure-only” (which behaves like “allow” over secure connections, but “suppress” over insecure ones). If sensitive attribute values are suppressed, then they will be stripped out of entries before they are returned to the client.
  • allow-in-filter — Indicates whether the specified attribute types are allowed to be used in search filters. The value may be one of “allow” (in which the server will permit searches including a sensitive attribute type in the filter), “reject” (in which the server will reject any search request with a filter that includes a sensitive attribute type), or “secure-only” (which behaves like “allow” over secure connections, but “reject” over insecure ones).
  • allow-in-add — Indicates whether the specified attribute types may be included in add requests. The value may be one of “allow”, “reject”, or “secure-only”.
  • allow-in-compare — Indicates whether the specified attribute types may be included in compare requests. The value may be one of “allow”, “reject”, or “secure-only”.
  • allow-in-modify — Indicates whether the specified attribute types may be included in modify requests. The value may be one of “allow”, “reject”, or “secure-only”.

The server includes a few sensitive attribute definitions that are defined but not enforced by default. One of these is the “Sensitive Password Attributes” definition, which can be used to ensure that the userPassword, authPassword, and ds-pwp-retired-password attributes are never returned to clients, cannot be included in search filters or compare requests, and can only be added or modified over secure connections. There are similar definitions for TOTP shared secrets, and also for one-time passwords and other single-use tokens.

To create a new sensitive attribute definition, you can use the dsconfig command-line tool or the web-based administration console. For example, let’s say that you have an attribute named employeeSSN that holds the social security numbers for your employees, and that you wanted to ensure that those values could never be retrieved from the server but could only be targeted by LDAP compare operations, and only over secure connections. You also want to ensure that writes to that attribute are only allowed over secure connections. The following dsconfig command could be used to accomplish that:

dsconfig create-sensitive-attribute \
     --attributeName "Employee Social Security Numbers" \
     --set attribute-type:employeeSSN \
     --set include-default-sensitive-operational-attributes:true \
     --set allow-in-compare:secure-only \
     --set allow-in-add:secure-only \
     --set allow-in-modify:secure-only \
     --set allow-in-returned-entries:suppress \
     --set allow-in-filter:reject

However, merely creating a sensitive attribute definition isn’t sufficient to ensure that it will be enforced. You also need to associate it with one or more client connection policies so that it will be enforced for clients assigned to those policies. The best way to do this is to configure the sensitive attribute in the global configuration so that it will apply across all client connection policies by default. You can do this with a dsconfig command like the following:

dsconfig set-global-configuration-prop \
     --set "sensitive-attribute:Employee Social Security Numbers"

There may be cases in which you want certain clients to be exempt from these restrictions (e.g., if you want to use the Ping Identity Synchronization Server to synchronize the Directory Server with some other data store). If such a need arises, you can create a client connection policy for that application and configure that policy to exclude the sensitive attribute restriction. You can do that with a command like:

dsconfig set-client-connection-policy-prop \
     --policy-name sync-server-policy \
     --set "exclude-global-sensitive-attribute:Employee Social Security Numbers"

Alternately, it is possible to only associate a sensitive attribute definition with a specific set of client connection policies so that it will not be enforced for other policies (using the sensitive-attribute property in the client connection policy configuration), but in most cases, it’s probably better to use the global policy to enable it across all policies by default and only exclude it for a specific set of policies.

Ping Identity Directory Server

Ping Identity Directory Server version has been released, and it’s available for download now, along with the companion Directory Proxy Server, Data Synchronization Server, Metrics Engine, and Server SDK products. It is a patch release that primarily addresses minor issues.

Because of an unfortunate glitch in the way that we generated the documentation, the updated release notes aren’t available on the website but are only included in the download itself. Here’s a list of the changes in the release:

  • Added a “Server Status Timeline” monitor entry that tracks the server’s last 100 status changes and the times that they occurred.
  • Updated LDAP external server monitor entries to include attributes for tracking state changes to the associated server, including the number of times the health state has changed, and timestamps and messages for the most recent state changes.
  • Improved Delegated Admin support for constructed attributes. Constructed attributes can now be read-only. Added an “Update Constructed Attributes” list to the REST resource type, which allows constructed attributes to be updated when their dependent attributes change. We also now handle constructed attributes that reference other constructed attributes.
  • Fixed an issue that could prevent switching between servers in the management console.
  • Fixed an issue that could require a large amount of memory when replaying a large subtree delete operation via replication. Also fixed a related issue that could cause the server to write a large number of mild error messages to the replication log.
  • Fixed an issue in which Delegated Admin may not behave correctly if the name of the REST resource type was not the same as the name for the resource endpoint.
  • Fixed an issue in which Delegated Admin search results could be truncated if the Directory Server was configured to disable syntax enforcement for an attribute type with a Boolean or integer syntax, and if an entry was encountered with an attribute of that type that had a value that did not conform to that syntax. The offending values are now omitted from the results, and a warning message is recorded in the server’s error log.
  • Fixed an issue in the Directory Proxy Server that could prevent the use of a custom entry placement algorithm (created with the Server SDK) in a version of the server built with alternate branding (for a reseller).
  • Updated the Groovy scripting language (that we use to support scripted extensions) to version 2.5.7.

Ping Identity Directory Server

We have just released version of the Ping Identity Directory Server, and it’s available for download now, along with the companion Directory Proxy Server, Data Synchronization Server, Metrics Engine, and Server SDK products. The release notes contain a blow-by-blow listing of the new features, enhancements, and fixes that it contains, but here are some of the highlights:

  • Added support for Red Hat Enterprise Linux 7.6, CentOS 7.6, Amazon Linux 2, and Windows Server 2019.
  • Added support for Docker 18.09.0 on Ubuntu 18.04 LTS.
  • Enable support for TLSv1.3 by default on JVMs that support it (which should be Java 11 and higher).
  • Added support for server profiles and a new manage-profile tool that can help install and manage the server using the DevOps “infrastructure as code” principle. A server profile can encapsulate the setup commands, configuration changes, Server SDK extensions, additional server root files, and other components of an installation, and it can be used in conjunction with orchestration frameworks to create a new instance with a given profile or to update an existing instance (for example, in a Blue/Green deployment) to apply a new profile.
  • Updated the server to support encrypting the contents of PIN files needed to unlock the certificate key and trust stores. If data encryption is enabled during setup, then the default PIN files will be automatically encrypted. We also updated the command-line tool framework so that files containing passwords can be encrypted, as well as the entire file.
  • Added a cipher stream provider that can be used to protect the contents of the encryption settings database with a key from the Amazon Key Management Service.
  • Added a cipher stream provider that can be used to protect the contents of the encryption settings database with a passphrase obtained from a HashiCorp Vault server.
  • Added a pass-through authentication plugin that can make it possible to authenticate accounts with credentials from the PingOne for Customers service.
  • Added support for insignificant configuration archive attributes. Updates to the configuration that only involve one or more of these attributes may not be permanently stored in the configuration archive to prevent it from growing too large over time. For example, if last login time tracking is enabled and does not explicitly exclude root users, then any time a root user authenticated, their entry could be updated with the new last login time, and that update could have previously been stored in the configuration archive. Such updates will no longer be archived by default.
  • Enabled assured replication by default for all add, delete, and modify DN operations. Enabled assured replication by default for all modify operations that alter passwords or key password policy state attributes. With these changes, the server will now delay the response to a matching write operation until it has confirmed that the change has been replicated to all local servers, up to a maximum delay of one second.
  • Updated the server to automatically remove references to obsolete replicas. A replica is obsolete when it has been disabled and all changes from it are older than the replication purge delay.
  • Updated the changelog and replication databases to add a target-database-size configuration property that makes it possible to control purging based on the size of the database in addition to the age of the changes that they contain.
  • Updated the behavior the server exhibits when it encounters a database reference to an attribute type definition that has been removed from the schema. In the 7.2 release, the server could fail when attempting to open a backend with a reference to a nonexistent attribute type. The server will now try to prevent the removal of attribute type definitions that are referenced by one or more backends, but if it does encounter a reference to a no-longer-existent attribute type, it will raise an administrative alert and continue processing the operation under the assumption that the missing attribute type uses a directory string syntax with case-ignore matching.
  • Added an HTTP servlet extension that can be used to retrieve the server’s current availability state. It accepts GET, POST, or HEAD requests sent to a specified endpoint and returns a minimal response whose HTTP status code may be used to determine whether the server considers itself to be AVAILABLE, DEGRADED, or UNAVAILABLE. This may be useful when routing HTTP requests through a load-balancer that can use requests of this type to assess the health of backend servers. It may also be helpful for orchestration frameworks that may wish to destroy and replace instances that become unavailable.
  • Added support for new plugin types that can be used to clean up directory entries for expired or inactive PingFederate persistent sessions.
  • Updated the server to prevent creating virtual attributes that attempt to generate values for the aci or ds-cfg-global-aci attribute types, as access control rules cannot be defined as virtual attributes. Also, prevented creating virtual attributes that attempt to generate values for the member or uniqueMember attribute types, as static group membership cannot be altered using virtual attributes.
  • Added logging for DNS lookups that take longer than a warning threshold (10 seconds by default). DNS resolution timing is also available in a new monitor entry.
  • Updated the topology management framework to make it easier to diagnose connection errors, including adding monitoring information for all failed outbound connections, and raising alarms and alerts when a server fails to connect to a peer server within a configured grace period.
  • Updated the dsreplication tool so that it can work with a node that is currently out of sync with the topology master.
  • Updated the dsreplication tool to allow removing a defunct server even if that server is currently online. Also, added the ability to automatically retry a failed attempt to remove a defunct server.
  • Updated the server to automatically remove a server from the topology when dsreplication disable is used to disable replication for the last non-schema domain.
  • Updated the encrypt-file tool to display a notice recommending the use of the --decompress-input argument when decrypting a file that also appears to be GZIP-compressed.
  • Updated the result code map to make it possible to override the default result code that the server returns when a client tries to perform a password-based bind against an account that does not have a password.
  • Updated the ldapdelete command-line tool to add support for client-side subtree delete, following referrals, deleting entries that match search filters, recording failures in a rejects file, rate limiting, and a variety of additional controls.
  • Updated the HTTP configuration so that the server no longer includes stack traces in generated error pages by default.
  • Updated the “Debug Trace Logger” and “File-Based Trace Logger” log publishers so that they exclude Admin Console activity by default.
  • Updated trace log publishers to support recording events related to access token validation.
  • Updated the file retention recurring task so that it no longer logs an informational message if there are no matching files to delete.
  • Added a correlation-id-response-header property to HTTP servlet extension configuration objects that can be used to set the response header used for correlation IDs. If set for a servlet extension, this value will override the value that would have otherwise been inherited from the HTTP connection handler.
  • Added an indent-ldap-filter tool that can make it easier to visualize the structure and components of a complex search filter.
  • Updated the setup utility to add a --skipHostnameCheck argument that can be used to bypass validation of the provided server hostname.
  • Updated the docs/build-info.txt endpoint to remove version information. That version information is now available in the build-info.txt file in the server root directory.
  • Updated the Directory Proxy Server to change the default load-balancing algorithm that it uses for directing client requests to backend servers. Previously, it always used a fewest operations strategy to send each request to the server with the smallest number of outstanding requests. The new strategy still chooses the server with the fewest outstanding operations for read operations but uses a failover strategy to consistently send write operations to the same server. When combined with the Directory Server’s default use of assured replication, this load-balancing strategy can dramatically reduce the likelihood of replication or uniqueness conflicts while minimizing the performance impact of a purely failover-based approach.
  • Updated the Directory Proxy Server to reduce the default maximum connection age from one hour to ten minutes. This should help avoid problems resulting from firewalls or other networking equipment that silently close connections that have been open for too long.
  • Update the Directory Proxy Server to add an index-priming-idle-listener-timeout property to the entry-balancing request processor configuration. This property specifies the maximum length of time that the server will wait for a response to an attempt to prime the global index before it will give up and retry the attempt.
  • Updated the Directory Proxy Server to reduce the likelihood of lock contention in health checks used to check the status of replication in a backend server.
  • Updated the Data Synchronization Server to support the PingOne for Customers service as a sync source. It was already possible to use PingOne for Customers as a sync destination.
  • Updated the Data Synchronization Server’s support for PingOne for Customers as a sync destination so that it is possible to specify a default population using the name of the population as an alternative to its ID. Population names can also be used in attribute mappings.
  • Updated the Data Synchronization Server to support Apache Kafka as a sync destination. Changes are provided as JSON-formatted representations of the entries before and after the change was applied.
  • Updated the Data Synchronization Server to make it possible to impose a rate limit on a sync pipe so that it does not adversely impact the performance of the destination server.
  • Updated the Data Synchronization Server to make it easier to construct JSON objects to store in the value of a specified attribute in the sync destination.
  • Updated the Data Synchronization Server to support LDAP filters that use extensible-match or approximate-match components. The Data Synchronization Server supports the same set of matching rules as the Directory Server.
  • Updated the Data Synchronization Server to add an attribute-comparison-method configuration property to sync classes. This property can be used to indicate whether to perform syntax-based or byte-for-byte comparisons when identifying what content was updated by a change.
  • Updated the Data Synchronization Server to add base64-encode-value and base64-decode-value properties to direct attribute mappings to facilitate synchronizing binary data.
  • Updated the Delegated Administration configuration. Delegated Admin Resource Types have been removed and replaced by REST Resource Types. Delegated Administrators and Delegated Group Administrators were removed and replaced by Delegated Admin Rights and Delegated Admin Resource Rights. When updating an existing server, older definitions will automatically be converted to their appropriate new versions.
  • Updated the Server SDK to make it easier to read and write data encrypted with keys from the server’s encryption settings database, and for obtaining information about the set of encryption settings definitions available in the server.
  • Updated the Server SDK to make it possible for a pre-parse bind plugin to convert a bind request from simple to SASL, or vice-versa. Added an example SASL mechanism handler that can be used to provide details of a successful or failed bind using attachments to an internal operation. Fixed an issue in the SASL bind result factory that could prevent a matched DN from being included in the response. Added the ability to include additional text in the access log message for an operation without having that text included in the response to the client.
  • Updated the Server SDK to provide support for access token validators for all types of products. It was previously only available for the Data Governance Server.
  • Improved the diagnostic message that the server returns when rejecting a proxied authorization attempt because the target account’s password policy state does not permit that user to authenticate.
  • Fixed an issue in which the server could incorrectly reject an attempt to change a user’s password in a single modify operation that included a delete modification with no values (indicating that all existing password values should be removed) followed by an add modification to supply the desired new password.
  • Fixed an issue that could cause an error when generating an encrypted LDIF export of a data set with a very large number of non-leaf entries. In such cases, the data is written to multiple files that are merged at the end of the process, but a problem could have prevented those files from being properly merged. This did not affect the usability or integrity of the exported data; it merely required the administrator to explicitly specify each of the files in the appropriate order when performing the import.
  • Fixed an issue in the access control handler in which it could incorrectly require the “export” and “import” rights for a modify DN request that includes a newSuperior that matches the DN of the entry’s current parent (which matches the behavior it exhibited for modify DN requests that did not include the newSuperior element). The export and import rights should only be required if the entry is being moved beneath a new parent.
  • Fixed an issue that could allow a modify operation to alter an entry in a way that left it without one or more of the superior object classes that it should have.
  • Fixed an issue in which changes to a dynamic group’s memberURL attribute sometimes did not take effect until after a restart.
  • Fixed an issue in which the server may not enter lockdown mode if it is missing replication changes that are no longer available in the topology, and if it has been restarted without addressing that problem.
  • Fixed an issue that could interfere with the operation of the stop-server.bat command on Windows systems configured with a locale that uses a comma instead of a period as the decimal separator.
  • Fixed issues that could interfere with the parsability of the periodic stats logger output when the server is run on systems configured with a locale that uses a comma instead of a period as the decimal separator.
  • Fixed an issue in which certain component initialization failure messages were written with a log level that was too low to prevent them from being recorded in the server’s error log by default, making it difficult to diagnose problems with those components.
  • Fixed the ordering of the consent-service-cfg.dsconfig batch commands so that bearer token authentication is enabled after the unprivileged consent on which it depends.
  • Fixed an issue that could cause a negative etime to appear in the access log when using assured replication.
  • Fixed an issue that could prevent dsreplication disable from removing replica IDs from the topology when one or more replication domains are disabled.
  • Fixed an issue that could cause the server to report an error when enabling or disabling a backend if there were any disabled notification managers defined in the server.
  • Fixed an issue in which the Directory Proxy Server could reject add attempts if all servers in an entry-balancing backend set had a health check state of DEGRADED or UNAVAILABLE.
  • Fixed an issue in which backups of the encryption settings database could be encrypted with a key from the encryption settings database.
  • Fixed an issue that could interfere with the ability to assign privileges via the mirror virtual attribute if the values to mirror were contained in another entry and were not accessible to unauthenticated clients.
  • Fixed an issue that could interfere with the ability to delete an entry containing uncached content if the LDAP changelog was enabled and configured to record changes in reversible form.
  • Fixed an issue that could prevent JMX clients from establishing SSL-encrypted connections.
  • Fixed an issue that could prevent HTTP-based connections from being associated with a client connection policy.
  • Fixed an issue in which a SCIM client was not permitted to add a member to a groupOfNames or groupOfEntries group.
  • Fixed an issue in which the startIndex value for a SCIM request could be incorrect if the server was configured with more than one base DN in the scim-resources.xml file.
  • Fixed an issue in which the config-diff tool may not identify differences that result from changing the order of values in an order-dependent property.
  • Fixed an issue in the Data Synchronization Server in which operational attributes may not be requested from an LDAP sync source if the LDAP filter was a nested filter.
  • Fixed an issue in the Data Synchronization Server in which a resync attempt could fail against Active Directory or PingOne for Customers when run with multiple passes.
  • Fixed an issue with the client-side validation properties that the haystack password validator would return in a get password quality requirements extended response. The values were human-readable descriptions of the validation properties rather than machine-parsable values.
  • Fixed an issue that could cause the server to encounter an internal error when processing a set subtree accessibility extended operation against an empty backend.
  • Fixed an issue in which a cryptographic error could interfere with inter-server authentication for sharing mirrored configuration data.
  • Fixed an installer issue in which the Admin Console’s trust store type could be set incorrectly if it was different from the key store type.
  • Disabled the fingerprint and subject attribute to user attribute certificate mappers by default for new installations (upgrades of existing installations will not be affected). These certificate mappers are rarely used and require the server to be configured with additional indexing before they can be used, and the lack of those indexes caused internal errors to be raised in the server on startup.

Managing Password Policy State in the Ping Identity Directory Server

The Ping Identity Directory Server offers a wealth of password policy functionality, and a lot of them require maintaining some kind of state information in the user’s entry. This includes things like:

  • The password policy by which the user is governed
  • The encoded passwords for the user
  • Whether the user’s account has been administratively disabled
  • When the user’s account will become active or will be deactivated
  • When the user’s password was last changed
  • When the user was first warned about an upcoming password expiration
  • Whether the user will be forced to change their password before being allowed to perform any other operations
  • A history of previous passwords for the user
  • Information about recent failed authentication attempts
  • Information about any grace logins used
  • The time the user last authenticated
  • The address of the client from which the user last authenticated
  • A retired password for the user

It’s often the case that an administrator application may want to obtain information about a user’s password policy state or to alter that state in some way. In this post, I’ll describe some of the options that the Ping Identity Directory Server provides to accomplish this.

Direct Manipulation of Operational Attributes

The password policy state for a user is maintained with operational attributes in the user’s entry, operating in conjunction with the password policy that governs that user. As such, you’d think that just altering the values of these attributes would be the best way to alter a user’s password policy state.

That is true for some of these password policy state attributes. The following attributes are supported for direct manipulation by applications and administrators:

  • ds-pwp-password-policy-dn — Specifies the DN of the configuration entry for the password policy that governs the user. If this is not specified, the user will be governed by the server’s default password policy. If it specifies the DN of an entry that does not exist or is not a password policy, then the user will not be permitted to authenticate.
  • ds-pwp-account-disabled — Indicates whether the user’s account is administratively disabled. If the attribute exists and has a value of TRUE, then the user account will be disabled and unable to authenticate. If the attribute is missing or has a value of FALSE, then the user account will not be considered disabled.
  • ds-pwp-account-expiration-time — Specifies a date and time (in generalized time format) that the user account will be considered expired and no longer able to authenticate. If this is not specified, the account will not expire. Note that account expiration is not the same as password expiration; account expiration is used for temporary accounts (e.g., for a contractor), whereas password expiration is used to require users to periodically change their passwords.
  • ds-pwp-account-activation-time — Specifies a date and time (in generalized time format) that the user account will become active. If this is specified, then the user will not be permitted to authenticate until after this time.

However, other operational attributes used to maintain password policy state are not directly writable by applications or administrator. These attribute type definitions are marked with the NO-USER-MODIFICATION constraint in the schema and are not considered part of the public interface that we expose. We may change the value format for these attributes, or even the attribute types, between releases without any prior warning. If you need to alter the password policy state in some other way, then you’ll need to use a different approach.

Resetting the User’s Password

There are several reasons that a user may not be allowed to authenticate. Some of them are directly controllable by an administrator using the operational attributes specified above. However, there are a number of other conditions that are not as directly controllable by administrators. These include:

  • The user’s password is expired
  • The user’s account has been locked because of too many failed authentication attempts
  • The user’s account has been locked because it has been too long since they last authenticated
  • The user’s account has been locked because they did not choose a new password soon enough after an administrative reset

Each of these conditions can be resolved by simply resetting the user’s password. Once the password has been reset, the account should immediately become usable.

Note that if the password is expired but the user still knows the right value, and if the allow-expired-password-changes property is set to true in the password policy that governs the user, then the user could change their own password using the password modify extended operation. This would presumably happen over an unauthenticated connection, but where the request includes the current password for the target user so it will be authorized as that user.

Also note that if the user’s account is only temporarily locked as a result of too many failed authentication attempts, then the user could just wait out the lockout specified by the lockout-duration property in the password policy. If they provide the correct password after the lockout duration has elapsed, they should be permitted to authenticate.

The Password Policy State Extended Operation

The password policy state extended operation is the Swiss Army knife of password policy state management in the Ping Identity Directory Server. Unlike the attributes that are used to actually store the information, this extended operation does provide a documented, stable, and supported interface for low-level manipulation of a user’s password policy state.

If you’re using the UnboundID LDAP SDK for Java, you can access this operation through the PasswordPolicyStateExtendedRequest, PasswordPolicyStateExtendedResult, and PasswordPolicyStateOperation classes, and the Javadoc for the request class has an example that demonstrates its use. If you’re using another API, then the Javadoc for the request class is still useful because it describes the OID and the value encoding for the operation.

Things that you can do with the password policy state extended operation include:

  • Retrieve the DN of the password policy that governs an account
  • Get, set, and clear the disabled state for an account
  • Get, set, and clear an account’s expiration time
  • Retrieve the length of time in seconds until an account will expire
  • Determine whether an account is expired
  • Get, set, and clear an account’s activation time
  • Retrieve the length of time in seconds until an account will become active
  • Determine whether an account is not yet active
  • Get, set, and clear the time that an account’s password was last changed
  • Determine whether an account’s password is currently expired
  • Retrieve the time that an account’s password will expire
  • Retrieve the length of time in seconds until an account’s password will expire
  • Retrieve the length of time in seconds until the account will be eligible to receive a warning about an upcoming password expiration
  • Get, set, and clear the time that an account was first warned about an upcoming password expiration
  • Get and set an account’s failure lockout state
  • Retrieve the time that an account was locked because of too many authentication failures
  • Get, update, set, and clear the set of authentication failure times for an account
  • Retrieve the length of time in seconds until a temporarily failure-locked account will be unlocked
  • Retrieve the number of failed authentication attempts until an account will be locked
  • Get, set, and clear an account’s last login time
  • Get, set, and clear an account’s last login IP address
  • Determine whether an account is currently locked because it has been too long since it last authenticated
  • Get the time that an account will be locked because it has been too long since it last authenticated
  • Retrieve the length of time in seconds until an account will be locked because it has been too long since it last authenticated
  • Get, set, and clear the “must change password” state for an account
  • Determine whether an account is currently locked because they failed to change their password in a timely manner after an administrative reset
  • Retrieve the time that an account was locked because they failed to change their password in a timely manner after an administrative reset
  • Retrieve the length of time in seconds until an account will be locked because they failed to change their password in a timely manner after an administrative reset
  • Get, update, set, and clear the set of grace login use times for an account
  • Retrieve the number of remaining grace logins for an account
  • Get, set, and clear the most recent “require change by time” value with which an account has complied
  • Retrieve the length of time in seconds that an account has to comply with a “require change by time” constraint in the password policy
  • Get the number of passwords in an account’s password history
  • Clear an account’s password history
  • Indicate whether an account has a retired password
  • Retrieve the time that an account’s former password was retired
  • Retrieve the time that an account’s retired password will stop being valid
  • Purge an account’s retired password
  • Retrieve a set of account usability errors, warnings, and notices
  • Determine whether an account is usable
  • Retrieves the set of SASL mechanisms that an account may use to authenticate
  • Retrieves the set of OTP delivery mechanisms that are available for an account
  • Determine whether an account has at least one TOTP shared secret
  • Add, remove, set, and clear the set of TOTP shared secrets for an account
  • Determine whether an account has at least one YubiKey OTP device registered
  • Get, update, set, and clear the public IDs of any YubiKey OTP devices that have been registered for an account
  • Determine whether an account has a static password set

The manage-account Command-Line Tool

While the password policy state extended operation is very powerful and flexible, you kind of need to write code to be able to use it. That’s fine if you’re writing an application that needs to be able to do this kind of thing, but not so much if you’re an administrator that just needs to update a user account. Fortunately, we offer a manage-account tool that gives you all of the functionality of the password policy state extended operation in a simple command-line utility.

This tool uses subcommands to indicate which password policy state functionality you want to invoke. The --helpSubcommands argument can be used to obtain a list of all of the available subcommands, but many of them are of the form “get-{property}”, “set-{property}”, or “clear-{property}”, like “get-account-is-disabled”, “set-account-is-disabled”, or “clear-account-is-disabled”. There’s also a “get-all” subcommand that displays the values of all password policy state attributes for a user.

The manage-account tool can operate on one or more entries, specifying them by DN (using the --targetDN or --dnInputFile arguments), by user ID (via the --targetUserID or --userIDInputFile arguments), or by an arbitrary filter (using the --targetFilter and --filterInputFile arguments). It can use multiple threads to process changes to multiple entries concurrently.

For example, to retrieve a list of all password policy state properties for a user, you might want to use a command like:

$ bin/manage-account --hostname \
     --port 636 \
     --useSSL \
     --bindDN uid=admin,dc=example,dc=com \
     --targetDN uid=test.user,ou=People,dc=example,dc=com
Enter the bind password:

dn: uid=test.user,ou=People,dc=example,dc=com
base-command-line: manage-account get-all --targetDN uid=test.user,ou=People,dc=example,dc=com
result-code: 0
result-code-name: success
get-password-policy-dn: cn=Default Password Policy,cn=Password Policies,cn=config
get-account-is-usable: true
get-password-changed-time: 20190610044447.266Z
get-account-is-disabled: false
get-account-is-not-yet-active: false
get-account-is-expired: false
get-password-is-expired: false
get-account-is-failure-locked: false
get-must-change-password: false
get-account-is-password-reset-locked: false
get-account-is-idle-locked: false
get-password-history-count: 0
get-remaining-grace-login-count: 0
get-has-retired-password: false
get-available-sasl-mechanisms: EXTERNAL
get-available-sasl-mechanisms: PLAIN
get-available-sasl-mechanisms: UNBOUNDID-CERTIFICATE-PLUS-PASSWORD
get-has-totp-shared-secret: false
get-has-registered-yubikey-public-id: false
get-has-static-password: true

Password Policy-Related Controls

We also provide support for a number of request and response controls that allow for interaction with a user’s password policy state. They include:

  • Account Usable Control — May be included in a search request to indicate that the server should include a corresponding response control with each matching entry that indicates whether the account has a password policy state that would allow that user to authenticate.
  • Get Password Policy State Issues Control — May be included in a bind request to indicate that the bind response should include a response control with information about any errors, warnings, or notices pertaining to a user’s password policy state.
  • Password Expired Control — May be returned in the response to a bind request if the target user has an expired password or must change their password before they will be permitted to request any other operation.
  • Password Expiring Control — May be returned in the response to a bind request if the target user’s password will expire in the near future.
  • Password Policy Control — May be included in an add, bind, compare, modify, and password modify requests to indicate that the server should return a response control with information about any potential error or warning related to the target user’s password policy state.
  • Password Update Behavior Control — May be included in an add, modify, or password modify request to customize the behavior that the server should use when setting a new password.
  • Password Validation Details Control — May be included in an add, modify, or password modify request to indicate that the server should return extended information about the quality of the proposed password and any issues that prevented it from being accepted.
  • Retire and Purge Password Controls — May be included in a modify or password modify request to indicate that the user’s former password should be explicitly retired (so that it can continue to be used for a brief period of time as an alternative to the new password) or purged.
  • Suppress Operational Attribute Update Control — May be included in a request to indicate that the server should suppress updates to one or more operational attributes (including last login time and last login IP address, which are normally controlled by the password policy) that may have otherwise been updated by the operation.

UnboundID LDAP SDK for Java 4.0.11

We have just released version 4.0.11 of the UnboundID LDAP SDK for Java. It is available for download from the releases page of our GitHub repository (, from the Files page of our SourceForge repository (, and from the Maven Central Repository (

The LDAP SDK release notes are available at, but the changes included in this release are as follows:

  • Updated the round-robin and fewest connections server sets so that they can temporarily blacklist a server that was found to be offline or unavailable. If an attempt to create a connection to a server fails, or if that connection is found to be unacceptable for some reason (e.g., it does not pass the associated health check), subsequent connection attempts will avoid that server until a background thread determines that it is available again. Blacklisted servers will still be tried as a last resort if it is not possible to get an acceptable connection to a non-blacklisted server. These server sets will now use the blacklist by default, but that can be disabled programmatically through the constructor or by setting a system property before creating the server set.
  • Updated the round-robin and fewest connections server sets to improve concurrency. In previous implementations, these sets could only create one connection at a time, which could limit the rate at which connection pools using them could establish new connections. This is no longer the case, and any number of threads will be able to create connections in parallel using the server sets. This change also updated the ServerSet API to make it possible for a server set to be notified whenever a connection created with that set has been closed.
  • Added a new SubtreeDeleter utility class that can make it easier to delete a specified subtree, optionally including or excluding the base entry for that subtree. It provides a good client-side alternative to the subtree delete request control, which isn’t supported by all servers and can sometimes be problematic in servers that do support it.
  • Added a new ldapdelete command-line tool that can be used to delete entries from an LDAP directory server. The DNs of the entries to delete can be provided on the command line, read from a file, or read from standard input. Alternately, the server can search for and delete all entries matching one or more filters. It offers a number of options, including support for client-side and server-side subtree deletes, rate limiting, and a variety of standard and proprietary controls.
  • Improved the LDAP SDK’s protection against socket write attempts that block for an indefinite length of time. This is only likely to occur when sending a large number of asynchronous requests over a connection, and only in the case that the server stops reading requests from the client or if a networking problem prevents the request from reaching the server and prevents the client from receiving any information about that failure.
  • Added InMemoryDirectoryServer.applyChangesFromLDIF methods that can be used to read LDIF change records and apply them to data in the server. The changes will be applied atomically, and if any of them cannot be applied successfully, then the server data will remain unchanged.
  • Updated the searchrate utility to allow specifying the base DN, scope, filter, and requested attributes using LDAP URLs rather than using separate arguments to provide appropriate values. The LDAP URL can be a fixed URL, or it can be a value pattern (including the ability to include variable content in the URLs or to load the URLs from a file). Using LDAP URLs allows for more precise control over the combination of base, scope, filter, and requested attributes on a per-request basis. Note that any addresses and ports used in the URLs will be ignored; the --hostname and --port arguments will still be used to identify which servers to use.
  • Updated the ldapsearch and ldapmodify command-line tools to use an unlimited response timeout, which will prevent the tool from giving up on an operation if it takes the server a long time to return any kind of response. Previously, the tools used the LDAP SDK’s default timeout of five minutes for searches and 30 seconds for add, delete, modify, and modify DN operations.
  • Updated the ldapmodify command-line tool to add a --clientSideSubtreeDleete argument that can be used to cause each delete operation to be converted to a client-side subtree delete operation, in which the tool will search for entries to delete and then delete them individually. This makes it easier to delete entries with subordinates on servers that either do not support the subtree delete request control or in which the client may not have permission to use that control.
  • Added a new indent-ldap-filter command-line tool that can help make it easier to visualize complex filters with a lot of components, and especially a lot of nesting. If possible, it can also try to simplify the filter (for example, to remove unnecessary levels of nesting, like an AND inside an AND).
  • Enabled concurrent socket factory use by default for all versions of Java. In the past, we have observed that at least some IBM JVMs had a thread safety issue with SSL socket factory implementations, so we only allowed a socket factory to be used concurrently by multiple threads on a whitelisted set of JVMs. We no longer believe that the IBM JDK socket factory thread safety is an issue, and there are now many more JVM vendors (e.g., Apple, Azul, Amazon Coretto, AdoptOpenJDK, and potentially Red Hat), so concurrent socket factory use will be enabled by default. If an issue is found on a particular JVM, then concurrent access can be disabled programmatically or with a system property.
  • Updated the LDAPCommandLineTool API to add an option to expose an --enableSSLDebugging argument. If this argument is available, and if it is provided in the set of command-line arguments when the tool is run, then the JVM’s SSL/TLS debugging support will be enabled, and the JVM will write a large amount of TLS-related debugging information to standard error. This can help troubleshoot problems with or provide detailed information about any TLS communication that the tool attempts.
  • Updated the LDAP SDK to add protection against JVM security managers that may prevent calls to certain methods, like attempts to interact with system properties, environment variables, or logger levels.
  • Updated the password reader so that it will generate a more user-friendly error message if it is run in a context in which no console is available. A tool could encounter this error if its output has been redirected, or if it’s not running in an interactive shell (for example, in a cron job or system startup script).
  • Dramatically improved the performance of the streamfile value pattern, which operates like the sequentialfile value pattern in that it can iterate through values in sequential order, except that streamfile doesn’t need to hold the whole file in memory at once whereas sequentialfile does.
  • Updated the Filter.simplifyFilter method to simplify an AND filter containing an LDAP false filter (an OR filter with zero components, which will never match anything) to just that LDAP false filter, and to simplify an OR filter containing an LDAP true filter (an AND filter with zero components, which will match any entry) to just that LDAP true filter.
  • Added a PasswordValidationDetailsResponseControl.get(LDAPException) method that makes it more convenient to get the response control from an unsuccessful operation.
  • Improved the exception message that is generated if a failure occurs while trying to create a TLS-based connection. If the JVM supports creating an unconnected SSLSocket and then connecting it after the fact (which makes it possible to specify a connect timeout), and that connection attempt failed (for example, because the client did not trust the certificate presented by the server), the LDAP SDK could think that the connection was still established. Subsequent attempts to use the connection would fail, but the failure message would not accurately reflect the true cause of the problem.
  • Updated the in-memory directory server to improve the diagnostic message that is returned when it rejects an add attempt because the provided entry is not within any of the configured base DNs.
  • Fixed an issue in generating the normalized representation of a multivalued RDN when one or more of those components referenced an attribute type by its OID or by a name other than the first one listed in the attribute type definition. Previously, the normalized string representation would have simply used an all-lowercase representation of the provided attribute name, but it will now use an all-lowercase representation of the primary name for that attribute (if schema information is available to the client). Also, updated the logic used to determine whether an RDN has a specified name or name-value pair to handle the use of alternate names, and exposed the RDN.getNameValuePairs method to make it easier to work with an RDN’s name-value pairs.
  • Fixed a bug in the ByteStringBuffer.append(CharSequence,int,int) method in which the final integer argument could be interpreted as the number of characters to append rather than the end position at which to stop appending, which could yield incorrect results when the method was called with a nonzero start position. Also, updated the ByteStringBuffer.append methods that take CharSequence arguments to eliminate the creation of an intermediate character array, thereby improving performance and reducing garbage creation.
  • Updated the LDAP SDK’s command-line tool framework to fix an issue with the tool’s validation for required, exclusive, and dependent argument sets. If an argument was configured with a default value, then that default value could have been mistakenly treated as if it had been explicitly provided by the user. This could cause problems for arguments that are part of an exclusive argument set (in which only one of the arguments in that set may be provided) or a dependent argument set (in which an argument can only be used if at least one of a specified set of additional arguments is present). In such cases, the tool could not have been used in interactive mode. The modrate tool was affected by this issue.
  • Updated the argument parser to fix a problem with the way that it handles backslash characters in argument property files. Previously, it only correctly handled backslashes if they were at the end of a line to indicate that the content continued to the next line, or if they were followed by the letter ‘u’ and the hexadecimal representation of the desired Unicode character. It did not handle the backslash in front of another character used to force that character to be treated as a literal (for example, a backslash followed by an equal sign should be treated as just an equal sign, but was instead being treated as a backslash followed by an equal sign).

Ping Identity Directory Server versions and

Ping Identity Directory Server versions and have been released. These are security updates, and customers running 7.x versions are strongly encouraged to upgrade.

The most important update included in these releases is a fix for a critical security issue introduced in the version that could cause certain passwords to be recorded in the clear on the server filesystem. There are two instances in which this could have occurred:

  • When creating an encrypted backup of the alarms, alerts, configuration, encryption settings, schema, tasks, or trust store backends, the backup descriptor was supposed to include the identifier of the encryption settings definition that was used to protect the contents of the backup. Instead of this identifier, the server would incorrectly include the password that backed that encryption settings definition. This issue did not affect backups of local DB backends (like userRoot), the LDAP-accessible changelog, or the replication database.

  • The server maintains a tool invocation log (logs/tools/tool-invocation.log), which keeps track of certain commands that are run on the system, especially those that may be used to alter the server configuration or data. Among other things, this tool includes the name of the tool and the arguments used to run it. Sensitive arguments, like those used to provide passwords, should automatically be redacted. However, if the tool is run with an argument that provides the path to a file containing a password, a bug could have caused the tool invocation log to record the contents of the first line of that file (which usually contains the password itself) rather than the path to that file. The following command-line tools were affected by this issue:

    • backup
    • create-initial-config
    • create-initial-proxy-config
    • dsreplication
    • enter-lockdown-mode
    • export-ldif
    • import-ldif
    • ldappasswordmodify
    • leave-lockdown-mode
    • manage-tasks
    • manage-topology
    • migrate-ldap-schema
    • parallel-update
    • prepare-endpoint-server
    • prepare-external-server
    • realtime-sync
    • rebuild-index
    • re-encode-entries
    • reload-http-connection-handler-certificates
    • reload-index
    • remove-defunct-server
    • restore
    • rotate-log
    • stop-server

Other tools were not affected by this second issue. Also note that this issue only involved passwords provided in files that were directly referenced as arguments on the command line. Passwords that were provided directly on the command line, and passwords that were automatically included because of their presence in a file, were properly redacted. Because of the nature of this issue, regular user passwords are not likely to have been exposed, but the passwords of administrators that may have run commands on the server system could have been recorded.

In both issues above, the passwords were written to a file on the server filesystem with permissions that made them only accessible to the account used to run the server. Other accounts on the system should not have been able to read the contents of those files. Nevertheless, if you believe that any passwords may have been compromised, we recommend taking the following steps to mitigate the risk:

  1. Update the server to a version that includes the fix for this issue. If you’re running version a 7.2 version, then you should upgrade to the release. If you’re running a 7.0 version, then you should upgrade to either version or version
  2. If you believe that any user passwords may have been exposed in the logs/tools/tool-invocation.log file, then change the passwords for those users and sanitize or delete that log file.
  3. If you believe that an encryption settings definition password may have been exposed in a backup descriptor, then create a new encryption settings definition, set it as the preferred definition for all subsequent encryption operations, export your data to LDIF, and re-import the data so that it is re-encrypted with the new definition. Create new backups, and destroy old backups with the compromised password.

In addition to fixing the bugs that led to the potential exposure of these passwords, we have added additional automated tests to help ensure that other problems like this do not occur in the future.

Other Changes Included in the Release

The following additional fixes have been included in the release:

  • Updated the behavior that the server exhibits if an attribute type is removed from the schema while that attribute type is still referenced by one or more server backends. In earlier releases, the server could fail to open a backend that referenced an attribute type that is no longer defined in the schema. The server will now permit the backend to be opened, but will generate an alert about any missing attribute type definitions on startup, and will also generate an alert on any access to an entry that contains a reference to a missing attribute type. The server will also attempt to prevent the removal of an attribute type that is still referenced by any of the backends.
  • Fixed an issue in which the stop-server.bat batch file may not function properly on Windows systems with a locale that uses a character other than a period as a decimal separator.
  • Fixed an issue in which the periodic stats logger output could have been difficult to parse on systems with a locale that uses a character other than the period as a decimal separator.
  • Fixed an issue that prevented creating a constructed virtual attribute for an attribute that was marked SINGLE-VALUE in the server schema.
  • Fixed an issue in which backups of the server’s encryption settings database could have been (automatically or explicitly) encrypted with a key from the encryption settings database.

Other Changes Included in the Release

The following additional fixes have been included in the release:

  • Added debug logging for DNS lookups that take longer than a configured length of time (10 seconds by default). A new “DNS Resolution” monitor entry is available to provide information about DNS lookups performed by the server.
  • Fixed an issue in which SCIM searches could have an incorrect startIndex value if the scim-resources.xml file was configured with multiple base DNs.
  • Fixed an issue that could cause an error while performing an encrypted LDIF export of a directory with a very large number of non-leaf entries. In such cases, the LDIF export will be split into multiple files, but the attempt to merge those files at the end of processing would fail. This error would not result in any data loss or exposure, and the exported data could still be imported by either providing all of the files to the import-ldif utility with separate –ldifFile arguments or by manually merging the files.

Naming Entries With entryUUID in the Ping Identity Directory Server

Choosing an entry’s RDN is something that shouldn’t be taken lightly. Ideally, it should meet all of the following criteria:

  • It needs to be unique so that it doesn’t conflict with the RDNs of any other entries beneath the same parent.
  • It should be something that’s not likely to change so that clients don’t have to worry about performing modify DN operations.
  • It should be something that doesn’t contain any personally identifiable or otherwise sensitive information. DNs are often included in log messages, and if a client has permission to see any part of an entry, then they’ll be able to see its DN.
  • It shouldn’t be something predictable. An attacker shouldn’t be able to guess the DN of a specific user, or even of any user in the server.

This means that things like usernames, common names, email addresses, and telephone numbers aren’t good choices. Account numbers are also not great because they tend to follow predictable patterns (e.g., sequentially increasing numbers).

What you really want is something that is basically random and has enough entropy to ensure that you won’t get an accidental conflict and so that an attacker will be unlikely to guess a valid value. It would be easy enough for a client to generate a long-ish random string to use for this purpose, but it turns out that the directory server (at least, a server that supports RFC 4530) already generates just such a value for each entry: its entryUUID.

Of course, there’s a catch-22 problem with using the entryUUID attribute as the naming attribute for an entry: the client doesn’t know what the entryUUID is going to be because it’s generated by the server. The client can’t specify it because the entryUUID attribute type is declared with the NO-USER-MODIFICATION constraint.

One potential workaround would be to create an entry with a throwaway value for the RDN, figure out what the entry’s entryUUID value is (using either the post-read control or by issuing a search to retrieve the entry), and issue a modify DN operation to rename the entry using that value. But that’s a hassle, and it puts undue burden on both the client and the server. Fortunately, if you’re using the Ping Identity Directory Server, then you have a couple of additional options:

  • The client can include the “name with entryUUID” request control in the add request.
  • The server can be configured so that any add request matching a specified set of criteria automatically gets created with entryUUID as its naming attribute.

Each of these will be described in more detail below.

The Name With entryUUID Request Control

The name with entryUUID request control may be included in an add request to indicate that the server should replace the RDN with the provided entry with one that uses the name and value of the entryUUID attribute the server generated for the entry. This control has an OID of “” and no value. We recommend that it be marked critical so that the add attempt will fail if the server cannot honor the request.

When using this control, the client should supply a DN for the entry that indicates the location in the DIT where the new entry should reside, but the RDN for the DN doesn’t really matter because it’s going to get replaced with the entryUUID. If you want, you can use an attribute value from the entry to add (just like if you were adding the entry without the control), but you can also use a bogus name-value pair. For example, you could provide a DN of “replaceWithEntryUUID=replaceWithEntryUUID,ou=People,dc=example,dc=com”, and the server would add the entry with a DN like “entryUUID=4869eea6-90bf-45bf-9fcb-eac096564bc8,ou=People,dc=example,dc=com” (although of course the entryUUID would vary each time).

Of course, there is one big issue with using this control: when the entry is added, the client won’t know what the entry’s actual DN really is. The way that we address that is to treat an add request that includes the name with entryUUID request control as if it also included a post-read request control with a single requested attribute of entryUUID. This will cause the add response to include a post-read response control with the DN and entryUUID value for the entry that was added. If you want additional attributes from the entry, you can explicitly include a post-read request control along with the name with entryUUID request control in the add request with the attributes you want to retrieve.

We provide support for the name with entryUUID request control in the ldapmodify command-line tool through the --nameWithEntryUIUD argument. For example:

$ bin/ldapmodify --hostname \
     --port 636 \
     --useSSL \
     --bindDN "cn=Name With entryUUID Example,ou=Applications,dc=example,dc=com" \
Enter the bind password:

The server presented the following certificate chain:

     Subject:,O=Ping Identity Self-Signed Certificate
     Valid From: Saturday, April 27, 2019 at 11:11:58 AM CDT
     Valid Until: Saturday, April 23, 2039 at 11:11:58 AM CDT
     SHA-1 Fingerprint: 41:5f:72:4a:e0:d0:22:18:3e:59:90:6f:65:fc:fe:34:f1:39:84:68
     256-bit SHA-2 Fingerprint: 54:d5:58:07:bd:af:8b:b4:19:8e:03:a3:c5:14:0d:2a:e6:1e:c2:3a:29:6c:17:5f:5f:61:97:1d:31:3d:2b:ac

WARNING:  The certificate is self-signed.

Do you wish to trust this certificate?  Enter 'y' or 'n': y
# Successfully connected to

dn: replaceWithEntryUUID=replaceWithEntryUUID,ou=People,dc=example,dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: test.user
givenName: Test
sn: User
cn: Test User
userPassword: testUserPassword

# Adding entry
# replaceWithEntryUUID=replaceWithEntryUUID,ou=People,dc=example,dc=com ...
# Result Code:  0 (success)
# Post-Read Response Control:
#      OID:
#      Post-Read Entry:
#           dn: entryUUID=7866e6d4-faa7-40e4-bad0-9ef26e566efd,ou=People,dc=exa
#            mple,dc=com
#           entryUUID: 7866e6d4-faa7-40e4-bad0-9ef26e566efd

Since the control doesn’t have a value, it’s easy enough to use in any LDAP API that supports controls (although you may find it a chore to get the DN of the resulting entry if that API doesn’t also support the post-read response control). But if you’re using the UnboundID LDAP SDK for Java, we provide direct support for the control through the NameWithEntryUUIDRequestControl class. I’ve written a simple AddEntryNamedWithUUID program to demonstrate how to use this class to add an entry with the request control and get its DN.

Automatically Naming Entries With entryUUID

Although it’s pretty simple to use the control in an add request to explicitly indicate that an entry should use entryUUID as the naming attribute, this does require the client to know about and use the control. This isn’t always possible, but the Ping Identity Directory Server has you covered there as well. You can configure the server so that any add request that matches a specified set of criteria will automatically be treated as if it included the name with entryUUID request control. This option is available through the following pair of properties in the global configuration:

  • auto-name-with-entry-uuid-connection-criteria
  • auto-name-with-entry-uuid-request-criteria

For example, if you wanted to configure the server so that any entry added with the “person” object class will behave as if it included the name with entryUUID request control, you would use a configuration like the following:

dsconfig create-request-criteria \
     --criteria-name "Adds of Person Entries" \
     --type simple \
     --set operation-type:add \
     --set "any-included-target-entry-filter:(objectClass=person)"

dsconfig set-global-configuration-prop \
     --set "auto-name-with-entry-uuid-request-criteria:Adds of Person Entries"

At this point, adding an entry with the “person” object class from any client will cause that entry’s RDN to be replaced with one generated based on the entryUUID operational attribute. The response will include the post-read response control as if the request had included the name with entryUUID request control (although the client will likely not know to look for it).

Ping Identity Directory Server

We have just released the Ping Identity Directory Server version, available for download at This is primarily a bugfix release, but it does offer a couple of significant new features. The release notes provide a pretty comprehensive overview of the changes, but the most significant updates are:

  • Fixed an issue that could cause an error during an LDIF export of a data set with a large number of non-leaf entries. In such cases, the LDIF data may be split into multiple files to make the LDIF process faster. If the data is split into multiple files, and if the LDIF export was encrypted, then an error may have prevented merging those files at the end of the export process. The exported data was still valid and could still be successfully imported, but with additional effort required.
  • Updated the LDAP pass-through authentication plugin to add an option to construct the DN to use to authenticate to the remote server from information in the local entry. Further, it is now possible to authenticate to the remote server with a bind DN value that may not be a valid LDAP distinguished name (for example, using the user principal name when passing through authentication to an Active Directory server).
  • Updated the LDAP pass-through authentication to add an included-local-entry-base-dn configuration property that makes it easier to identify which local users for which pass-through authentication may be attempted. If pass-through authentication is enabled, it will no longer be attempted by default for root users or topology administrators.
  • Fixed a number of issues in the LDAP pass-through authentication plugin. It will now use separate connections for search and bind operations. It will now make better use of multiple servers for improved availability, and can re-try a failed operation when only a single server is configured. Improved the troubleshooting information that is available when a problem is encountered during pass-through authentication processing.
  • Fixed an issue that could cause entryUUID mismatches across servers if the server is configured to automatically use entryUUD as the naming attribute for entries matching a given set of criteria.
  • Updated the server to ensure that information about missing replication changes persistent across restarts. If the server has been offline for longer than the replication purge delay, then replication will be unable to automatically bring that server back in sync with the other servers in the topology. However, if the server had been restarted after that problem was identified, the record of the missing changes could be inadvertently cleared.
  • Updated the dsreplication tool to allow enabling replication on a node whose topology information is out of sync with the topology master.
  • Updated the topology manager to make it easier to diagnose connection errors between servers in the topology.
  • Added logging for DNS lookups that take longer than expected to complete (10 seconds by default). This can make it easier to identify problems with DNS issues cause connectivity problems or slowness.
  • The delegated administration configuration has changed significantly. When updating an existing installation, the update tool will automatically convert the old configuration model to the new one.
  • The Data Synchronization Server has been updated to support bidirectional synchronization with the PingOne for Customers hosted directory service. The release added support for the PingOne for Customers service as a sync destination. With the release, it is now also possible to use PingOne for Customers as a sync source.

So I guess SLAMD is a thing again…

The year was 2002. I had recently jumped ship from Netscape to Sun Microsystems after AOL bought Netscape and decided they wanted out of their iPlanet alliance. I was working as a sustaining engineer on whatever Sun’s brilliant marketeers decided to call their LDAP directory server at the time. One day, my boss, Steve Shoaff, came into my office with a couple of ideas. He said that he wanted me to build a tool that could measure the directory server performance with a lot of load by hitting it from multiple clients at the same time. And he said that he wanted to call it “SLAMD”, which is a play on “slapd”, which kind of stands for “standalone LDAP daemon” and is used in the process names of some directory server products.

So I built it, and I think that it’s fair to say that it turned into something substantially more impressive than either of us originally imagined. It had a Java-based API that you could use to define the types of workloads that you wanted to process, a web-based interface that you could use to schedule jobs and view the results (numerically and graphically), and a client that you could install on the systems that you wanted to used to drive load against the server. Over time, I added new types of jobs and lots of other features, like self-optimizing jobs (which repeatedly run the same job with different amounts of client load to find the optimal performance), job groups (which let you schedule several jobs to run in succession), and resource monitoring (which lets you monitor system statistics like CPU, disk, and network utilization on various systems).

SLAMD was pretty good at what it did, and it worked with all types of LDAP-compliant directory servers, so it became one of the preeminent directory server benchmarking tools. We convinced Sun to open source it, and lots of people started using it. It could be used for things other than directory servers, too (I did build some basic support for other protocols like HTTP, POP3, IMAP, and SMTP, and the ability to interact with relational databases), but LDAP performance and stress testing was always its big wheelhouse.

Fast forward several years, and SLAMD was still pretty great but was starting to show its age, at least under the covers. I started working on it in the Java 1.3 days, before nice features like generics, foreach, concurrency APIs, sub-millisecond timing, and so much more. The web interface was all hand-crafted HTML and mostly contained in one giant source file, and it was getting pretty unwieldy. I did make some attempts to try to modernize it, but I got really busy with other things, like creating OpenDS as a replacement for Sun’s stagnating C-based directory server, then moving on from Sun to launch UnboundID and working furiously to build up its directory server, directory proxy server, and LDAP SDK products.

Shortly after Sun and I parted ways, Oracle bought Sun and gradually started killing off most of the good things about it. This included shutting down the site, which had been the open source repository for SLAMD, and I decided to take that opportunity to just let it kind of fade away. I figured it might be better to start something new from scratch, with a much more modern foundation, than to try to give SLAMD the kind of makeover I thought it needed. Of course, that was nearly a decade ago, and while I’ve done a lot since then, creating a new directory server benchmarking tool (other than a handful of command-line tools like searchrate and modrate that we ship with the LDAP SDK) hasn’t really been in the cards. Meanwhile, SLAMD is still getting a surprising amount of use. Even though it’s not so easy to get your hands on it anymore, people were still getting their hands on it and using it.

After having the topic come up several times in the last few weeks, I finally bit the bullet and dusted off the old code. I spent a couple of weekends doing some pretty extensive code cleanup. I fully generified everything, so there aren’t any more build warnings about raw types. I pulled in much more modern versions of dependencies like Apache Tomcat, the Berkeley DB Java Edition, and the UnboundID LDAP SDK for Java. I reorganized some of the jobs, including putting some legacy stuff out to pasture, and I wrote new versions of several of them. I split up some of the admin interface code into separate source files to make it more manageable, and I made some minor user interface enhancements.

So anyway, I went ahead and put the updated code on GitHub at Since no single entity owns the copyright on the code, it’s not possible to change the license, and it will therefore always will be licensed under the terms of the Sun Public License version 1.0. I’m not promising that I’ll add any major new features, but it’ll at least be more readily available than it has been, and with some more modern guts.

For now, if you want to use it, you’ll need to check it out and build it for yourself (there’s a README that tells you how to do that). Just know that it’s not backward-compatible with the version that I last touched in 2010, so don’t try to upgrade an existing instance (but if you do want the code for that old version, just check out revision 5777f3e5d78ff03985af4e68670e649127339c59, since I used it to seed the new repository).

Also note that there’s still a lot more work to do. There’s quite a bit more code cleanup that’s still on my to-do list (it builds cleanly with Java 8, but there are several deprecation warnings with Java 11). I plan on rewriting some more of the jobs (including making some potentially-incompatible changes). I know that some of the resource monitoring is broken (at least on Linux, which isn’t so concerned about maintaining consistent output in some of its commands). I haven’t touched any of the documentation. I’ve only done a very minimal amount of testing so far. So while it’s fine to play around with what’s there now, and please report issues if you find them, just know that I reserve the right to make even more non-backward-compatible changes as I continue to modernize the code.