Changing an expired FreeIPA password is complicated, particularly when not using SSH, kpassword or other command line tools. Here is a Java method which allows expired and non-expired password changes with no command line access required.


The password change can be accomplished using an extended LDAP request which is authenticated by supplying the current password in the request. I have not found a method for using Kerberos to change an expired password.


This Java code works for a FreeIPA server for users with either expired, or non-expired passwords. You must use an SSL connection to the LDAP server via port 636. The library used is the UnboundID LDAP SDK.


import com.unboundid.ldap.sdk.*
public void changePassword(String user, char[] currentPassword,
char[] newPassword) throws Exception {
try {
// SSL connection required to change password
SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
SSLSocketFactory socketFactory = sslUtil.createSSLSocketFactory();
LDAPConnection connection = new LDAPConnection(socketFactory,
"example.com", 636);

String userDN = "uid=" + user
+ ",cn=users,cn=accounts,dc=example,dc=com";
// Cannot use char[] for the bind, have to use String
String curPwd = new String(currentPassword);
String newPwd = new String(newPassword);
// Authenticate the bind
SimpleBindRequest bindRequest =
new SimpleBindRequest(userDN, curPwd);
// Bind to the ldap server using the password
BindResult bindResult = connection.bind(bindRequest);

// Change the password
PasswordModifyExtendedRequest passwordModifyRequest =
new PasswordModifyExtendedRequest(userDN,
curPwd, newPwd);
PasswordModifyExtendedResult passwordModifyResult =
(PasswordModifyExtendedResult) connection.processExtendedOperation(passwordModifyRequest);
// NOTE: The processExtendedOperation method will only throw an exception
// if a problem occurs while trying to send the request or read the
// response. It will not throw an exception because of a non-success
// response.
if (passwordModifyResult.getResultCode() != ResultCode.SUCCESS) {
throw new Exception("Password Change Failed:"
+ passwordModifyResult.getDiagnosticMessage());
}

// Close the context when we\'re done
connection.close();

} catch (LDAPException ne) {
if (ne.getResultCode() == ResultCode.INVALID_CREDENTIALS) {
throw new Exception("Invalid Credentials");
}
throw new Exception("Login Failed");

} catch (GeneralSecurityException ie) {
throw new Exception("Login Failed");
}
}

And now users can be prompted when their passwords have expired and are allowed to change them via a web form.


One problem remains: No password checking is performed for users with expired passwords. i.e. The initial login attempt returns a Kerberos exception along the lines of 'CREDENTIALS_EXPIRED' regardless of the password entered. Obviously the correct password is required to update their password, but this still reveals a little more information than is strictly necessary.