pingidentity / ldapsdk

UnboundID LDAP SDK for Java
Other
334 stars 81 forks source link

Simple bind fails if password contains latin character #165

Closed msps1 closed 5 months ago

msps1 commented 5 months ago

If the password contains latin character, say 'UnMiercolesporlamañana.11', then simple bind fails with unboundid library. This is for Active Directory.
LDAPConnection ldapConnection = ... SimpleBindRequest bindRequest = new SimpleBindRequest(userId, password); ldapConnection.bind(bindRequest);

Steps to recreate:

  1. Create a user in Active Directory with password as 'UnMiercolesporlamañana.11'
  2. Use the sample code above to create LDAPConnection and do simple bind
  3. It fails with error 'Invalid credentials'

Issue even in the latest 7.0.0 release.

The same works with LDP tool and also works when Java JNDI library is used.

dirmgr commented 5 months ago

This is very likely not an issue with the LDAP SDK itself, but is more likely a problem that results from different character encodings.

LDAP specifications state that strings in LDAP should always be encoded using UTF-8, and that is the encoding that the LDAP SDK always uses. However, it's probably not the default encoding that your desktop system uses, and many clients incorrectly just use the default character encoding from the underlying system instead of forcing UTF-8 like they're supposed to.

My guess is that when the entry was created, the client probably used an alternative encoding the like ISO 8859-1 (also known as Latin-1). This encoding is exactly the same as UTF-8 when representing ASCII characters, but is different for non-ASCII characters. So if the password uses non-ASCII characters, and if the client that created the account used an encodings other than UTF-8, then bind requests will only succeed for that password if they use that same encoding, and the LDAP SDK’s correct use of UTF-8 won't work

To see if that's the case, there are a couple of things you could try. One would be to try forcing an alternative encoding when providing the password to the LDAP SDK. You can do that by providing the password as a byte array rather than a string, and getting that byte array with a method like String.getBytes(“ISO-8859-1”). If you provide the password as a byte array rather than as a string, then the LDAP SDK will use exactly those bytes in the bind request instead of converting the provided string to bytes using UTF-8.

The other way that you can confirm that it's an issue with character encoding is to intercept the bind request from a client that works and examine the bytes that make up that request. You can do that with the ldap-debugger tool that is provided with the LDAP SDK. If you can use it to capture the client request for a successful bind, you can see how the non-ASCII characters are encoded, and check them against the expected UTF-8 encoding for that character.

It's also possible that the client is using the same character encoding, but that the character has been composed differently. Some characters, like the lowercase n with a tilde over it, actually have multiple ways to express then in Unicode (and therefore UTF-8). There is a single Unicode character that represents the lowercase n with tilde, but there's also a second way to compose it, which is as an ASCII letter n followed by a special combining character that basically means “put a tilde over the previous character”. Those two compositions have different Unicode representations, and therefore different UTF-8 encodings. The server is supposed to normalize Unicode strings when it receives them, but that might not be happening, which means that only clients that use the same composition that was used to actually store the character would be able to successfully bind. You could also check that when the ldap-debugger tool, since it would let you see exactly what bytes the client sent and compare that with what the LDAP SDK is sending.

You can force the LDAP SDK to send the password with whatever representation you want by providing it as a byte array rather than a string, but the LDAP SDK should already be doing the correct thing whenever it encodes the bind request to send to the server, but if a former client or the server itself didn't do the right thing when setting or checking the password, that might require forcing the LDAP SDK to do the same incorrect thing.

msps1 commented 5 months ago

Thanks for the response. Yes, it was issue with character encoding. Used the String.getBytes("UTF-8") and passed the password as byte array. It worked. Sorry, for not checking it correctly.