I recently had to setup a AWS Simple AD to replace an old OpenLDAP that served us for years. I may post more about this in the following days, but if you have interest in any specific topic, please post here in the comments and I can speed it up ;)
Before
During this migration, I had to adjust a simple app I did in the past to reset and change users' passwords. To set a user's password, this application was just calculating the new password and setting in the
userPassword attribute, like this:
LdapConnection connection = new LdapNetworkConnection( ldapserver, 389, false )) {
connection.bind(user, pass);
EntryCursor cursor = connection.search( baseDN, "(uid=" + user.getUsername() + ")", SearchScope.ONELEVEL, "*" );
if ( !cursor.next() ) {
logger.debug("no user with uid '{}' found.", user.getUsername());
return false;
}
Entry entry = cursor.get();
byte[] newPass = PasswordUtil.createStoragePassword(user.getPassword(), LdapSecurityConstants.HASH_METHOD_SSHA);
entry.put("userPassword", newPass);
entry.put("shadowLastChange", new Date().getTime() / (1000 * 60 * 60 * 24) + "");
logger.debug("changing password for user '{}'...", user.getUsername());
connection.modify(entry.getDn(),
extractModification("userPassword", entry),
extractModification("shadowLastChange", entry)
);
connection.unBind();
The problem
When checking the Simple AD user's attributes, there was no
userPassword anymore, so I tried to find a way to change the password.
As I was already using de
Apache LDAP API, and it supports
LDAP Password Modify Extended Operation, it seemed a good way to go as I wouldn't need to worry about implementation-specific problems.
I just forgot to check if Simple AD/Samba4 supports it :( And it didn't, AFAICS.
Believe me, I tried a lot of code combinations to use the 1.3.6.1.4.1.4203.1.11.1 extended operation as
there is no doc (and even
reported a bug), but could not make it work.
There is hope
After spending a lot of time trying to figure it out, I found this archive:
http://linux.samba.narkive.com/3nPys6p5/samba-sambar4-user-creation-with-ldap-and-initial-password
There, Tomas Mueller says:
I do not have a AD available today , i'll try tomorrow. i've found this
about the userPassword attribute on msdn:
http://msdn.microsoft.com/en-us/library/cc223249(prot.20).aspx
<http://msdn.microsoft.com/en-us/library/cc223249%28prot.20%29.aspx>
searching the sourcecode about userPassword i've found this comment in
password_hash.c:
* Notice: unlike the real AD which only supports the UTF16 special based
* 'unicodePwd' and the UTF8 based 'userPassword' plaintext attribute we
* understand also a UTF16 based 'clearTextPassword' one.
* The latter is also accessible through LDAP so it can also be set by
external
* tools and scripts. But be aware that this isn't portable on non
SAMBA 4 ADs!
"The latter is also accessible through LDAP" implies that unicodePwd and
userPassword aren't.
- Thomas
So, after reading this, I tried
clearTextPassword and it worked!!! It seems that Samba4 doesn't implement the extended operation, but created this fake attribute to allow password set through LDAP protocol.
Follows the base of the final code:
LdapConnection connection = new LdapNetworkConnection( ldapserver, 389, false )) {
connection.bind(user, pass);
SearchRequest searchRequest = getUserSearch(user);
SearchCursor cursor = connection.search( searchRequest );
if ( !cursor.next() ) {
logger.debug("no user with sAMAccountName '{}' found.", user.getUsername());
throw new InvalidCredentialsException();
}
Entry entry = cursor.getEntry();
Dn userBeingChangedDN = entry.getDn();
logger.debug("changing password for user '{}'...", user.getUsername());
entry.put("clearTextPassword", newPassword.getBytes("UTF-16LE"));
connection.modify(entry.getDn(), extractModification("clearTextPassword", entry));
try (LdapConnection connectionForChange = getConnection()) {
connectionForChange.bind(userBeingChangedDN, user.getPassword());
connectionForChange.unBind();
} catch (LdapException e) {
throw new InvalidCredentialsException();
}
connection.unBind();
Hope it helps!
If you want to know more about the whole Simple AD setup, please comment asking what you want.
Extras
private static Modification extractModification(String attrAlias, Entry modifiedEntry) {
Modification mod = new DefaultModification();
mod.setOperation(ModificationOperation.REPLACE_ATTRIBUTE);
Attribute attribute = modifiedEntry.get(attrAlias);
mod.setAttribute(attribute);
return mod;
}
private SearchRequest getUserSearch(User user) throws LdapInvalidDnException, LdapException {
SearchRequest searchRequest = new SearchRequestImpl();
searchRequest.setBase( new Dn("CN=Users,DC=visagio,DC=company") );
searchRequest.setFilter( "(sAMAccountName=" + user.getUsername() + ")" );
searchRequest.setScope( SearchScope.ONELEVEL );
searchRequest.addAttributes( "*" );
return searchRequest;
}