Saturday, October 30. 2021
Java ldappasswd implementation for the password modify extended operation


A quick entry this time to present a java implementation of the ldappasswd command tool. The ldap protocol by default performs a password change just sending the modification of the userPassword attribute, just like any other attribute. This behavior was considered not safe and an extended operation was standardized via rfc3062 soon. The extension allows to incorporate the typical requirements to the password change: ensure that it is done under adequate security conditions; allow sending the old password to validate it at modification time; generate a random password and return it to implement a reset... The final name of the extension is LDAP Password Modify Extended Operation (oid 1.3.6.1.4.1.4203.1.11.1) and nowadays it is supported by the majority of the servers. But the JNDI client does not include this extension and, although there are several implementations out there, I decided to implement it along with an ldappasswd counterpart just using plain JavaSE. This entry is the result of that adventure.
The post is not going to comment the java implementation in detail. The code consists in two classes for the standardized request and response (basically the management of the ASN.1 for the fields needed in both parts) and another one for the command. To use the extensions just create an LdapContext normally and call the extendedOperation method passing the initialized request. The LdapPasswd main class executes the same to perform the password change, so you can also check it if you want an example.
LdapContext ctx = new InitialLdapContext(env, null);
PasswordModifyExtendedRequest passwordModifyRequest = new PasswordModifyExtendedRequest(
"uid=ricky,ou=People,dc=sample,dc=com", "old-password", "new-password");
PasswordModifyExtendedResponse passwordModifyResponse =
(PasswordModifyExtendedResponse) ctx.extendedOperation(passwordModifyRequest);
The complete maven project can be downloaded from here and the usage for the main class is more or less the same than the one used in the UNIX package.
unzip ldappasswd.zip
cd ldappasswd
mvn clean package
java -jar target/ldappasswd-1.0.0-SNAPSHOT.jar -h
Exception in thread "main" java.lang.IllegalArgumentException:
USAGE: java -jar ldappasswd.jar [-H ldapuri] [-D binddn] [-W] [-w passwd] [-A] [-a oldPasswd] [-S] [-s newPasswd] [-Z] [user]
OPTIONS:
-H <ldapuri>: DN of the admin user to connect with (default: "ldap://localhost:389")
-D <binddn>: DN of the admin user to connect with (ex: "cn=Directory Manager")
-W: Promp for the password of the admin user to connect with
-w <passwd>: Password of the admin user to connect with
-A: Promp for the old password
-a <oldPasswd>: Old password of the user
-S: Promp for the new password
-s <newPasswd>: New password of the user
-k: insecure connection (accepts any certificate)
-Z: StartTLS before connecting.
-h: Prints this help.
ARGUMENTS:
[user]: DN of the user to change the password (if none it will be changed to the connected user)
Once the tool is ready it is time for the ldap server. This time the 389 Directory Server implementation is going to be used. This ldap software is also a derivative of the old netscape server and it is available in all linux distributions. I decided to install a Debian 11.1 in a VM and quickly setup the package. The installation and configuration is easy.
apt-get install 389-ds
dscreate interactive
Selinux support will be disabled, continue? Yes
Enter system hostname [debian11]:
Enter the instance name [debian11]:
Enter port number [389]:
Create self-signed certificate database [yes]:
Enter secure port number [636]:
Enter Directory Manager DN [cn=Directory Manager]:
Enter the Directory Manager password: password
Confirm the Directory Manager Password: password
Enter the database suffix (or enter "none" to skip) [dc=debian11]: dc=sample,dc=com
Create sample entries in the suffix [no]:
Create just the top suffix entry [no]: yes
Do you want to start the instance after the installation? [yes]:
Are you ready to install? [no]: yes
The software is installed and one instance is created using the default ports and a self-signed certificate. Then a user is added in order to perform the tests with it.
ldapmodify -h localhost -p 389 -D "cn=Directory Manager" -W
dn: ou=People,dc=sample,dc=com
changetype: add
objectclass: organizationalUnit
ou: People
adding new entry "ou=People,dc=sample,dc=com"
dn: uid=ricky,ou=People,dc=sample,dc=com
changetype: add
objectclass: inetorgperson
uid: ricky
sn: ricky
cn: ricky
userpassword: passsword
adding new entry "uid=ricky,ou=People,dc=sample,dc=com"
Now the server is congured to allow users to change their own passwords. An ACL is needed, which should be added to the tree.
ldapmodify -h localhost -p 389 -D "cn=Directory Manager" -W
dn: dc=sample,dc=com
changetype: modify
add: aci
aci: (targetattr = "userPassword") (version 3.0; acl "allow userpassword self modification"; allow (write) userdn = "ldap:///self";)
One more command is presented to show that 389-ds supports the password modify extension.
ldapsearch -h localhost -p 389 -D "cn=Directory Manager" -W -s base -b "" supportedExtension | grep 1.3.6.1.4.1.4203.1.11.1
supportedExtension: 1.3.6.1.4.1.4203.1.11.1
Finally the certificate used by the server was obtained and saved in debian11.p12 trust-store with password changeit. Time to start showing the ldappasswd command with different arguments.
Auto change the password for the same user passing the previous and new password:
java -Djavax.net.ssl.trustStore=debian11.p12 -Djavax.net.ssl.trustStorePassword=changeit -jar target/ldappasswd-1.0.0-SNAPSHOT.jar -H ldaps://debian11:636 -D "uid=ricky,ou=People,dc=sample,dc=com" -w password -a password -s new-password # Using LDAP url ldaps://debian11:636... # Connecting as user uid=ricky,ou=People,dc=sample,dc=com... # Performing password change...
Auto change the password with previous password but generating the new one:
java -Djavax.net.ssl.trustStore=debian11.p12 -Djavax.net.ssl.trustStorePassword=changeit -jar target/ldappasswd-1.0.0-SNAPSHOT.jar -H ldaps://debian11:636 -D "uid=ricky,ou=People,dc=sample,dc=com" -w password -a password # Using LDAP url ldaps://debian11:636... # Connecting as user uid=ricky,ou=People,dc=sample,dc=com... # Performing password change... # generated passwd: n02TRMuu
Using an administrator and only sending the new password:
java -Djavax.net.ssl.trustStore=debian11.p12 -Djavax.net.ssl.trustStorePassword=changeit -jar target/ldappasswd-1.0.0-SNAPSHOT.jar -H ldaps://debian11:636 -D "cn=Directory Manager" -w password -s new-password uid=ricky,ou=People,dc=sample,dc=com # Using LDAP url ldaps://debian11:636... # Connecting as user cn=Directory Manager... # Performing password change...
Using an admin user but performing a start SSL over the port 389.
java -Djavax.net.ssl.trustStore=debian11.p12 -Djavax.net.ssl.trustStorePassword=changeit -jar target/ldappasswd-1.0.0-SNAPSHOT.jar -H ldap://debian11 -Z -D "cn=Directory Manager" -w password -s new-password uid=ricky,ou=People,dc=sample,dc=com # Using LDAP url ldap://debian11... # Performing the StartTLS with user cn=Directory Manager... # Performing password change...
The operation cannot be done without TLS. This is enforced by the server.
java -Djavax.net.ssl.trustStore=debian11.p12 -Djavax.net.ssl.trustStorePassword=changeit -jar target/ldappasswd-1.0.0-SNAPSHOT.jar -H ldap://debian11 -D "cn=Directory Manager" -w password -s new-password uid=ricky,ou=People,dc=sample,dc=com # Using LDAP url ldap://debian11... # Connecting as user cn=Directory Manager... # Performing password change... Exception in thread "main" javax.naming.AuthenticationNotSupportedException: [LDAP: error code 13 - Operation requires a secure connection. ]; remaining name '' at ...
Please note that other servers can act differently. For example the attached maven project performs some tests using apache-ds and it does not generate any password in the response. Therefore with the apache software the new password is compulsory (-s or -S options in the command). As the RFC mentions, the server can not recognize or support the combination of the provided fields (user-dn, old and new password), in that case it should not proceed with the password change and return an error.
That is all for today. If, for whatever reason, you need the ldap modify password extension in a Java project, here you have an example that requires nothing except JavaSE. Feel free to re-use or extend the code as you need.
Extended regards!
Comments