blob: 073f88cd04d67762ce99c7e946eafd3ddec621ff [file] [view]
---
title: 2.2 - Binding and unbinding
navPrev: 2.1-connection-disconnection.html
navPrevText: 2.1 - Connection and disconnection
navUp: 2-basic-ldap-api-usage.html
navUpText: 2 - Basic LDAP API usage
navNext: 2.3-searching.html
navNextText: 2.3 Searching
---
# 2.2 - Binding and unbinding
In **LDAP**, if one wants to access the server data, the common way to do it is to bind to the server. However, it's important to understand that binding is different from connecting.
Creating a connection to an **LDAP** server opens a socket between the client and the server. You must provide the address and the port in order to do this.
The **bind** operation, on the other hand, creates a _LdapSession_ which will hold user's information for the duration of the session. This information are limited.
But it's important to know that it's possible to _bind_ anonymously, which doesn't require a user or password, and still be able to send requests to the server (although the server can forbid anonymous binds).
Once the user has finished interacting with the server, they can _unbind_, destroying the session held on the server. This operation does not close the connection, because, again _bind != connection_!
## Binding
There are two possible types of binds in *LDAP*:
* **Simple**
* **SASL**
The first one is based on a user/password sent to the server, which verifies the credentials are valid. It's also possible to proceed with an anonymous bind explicitly.
The second type is more complicated, and is used whenever authentication uses a specific mechanism, like **DIGEST-MD5**, **Kerberos** or certificate based is required.
### Simple Bind
One can issue three kinds of simple bind:
* _anonymous bind_
* _name/password bind_
* _unauthenticated authentication bind_
The first one is the easiest, but depending on the server's configuration, will be accepted or rejected (not all servers allow anonymous binds)
Most of the time, the _bind_ operation will not return anything but a success or an error result code. You either get bound, or will receive an _LdapException_ if an error occurs, so there is no need to proceed with the returned _BindResponse_.
Issuing an anonymous bind is simple, you neither provide a user nor a password:
```Java
@Test
public void testAnonymousBindRequest() throws Exception
{
connection.bind();
}
```
Issuing a user/password bind is slightly more complex, because those credentials must be included:
```Java
@Test
public void testSimpleBindRequest() throws Exception
{
connection.bind( "uid=admin,ou=system", "secret" );
}
```
>**Note** It's important to note that the user's name is a *[Dn](6.9-dn.html)*, not a simple name like 'John Doe"
Last, not least, there is a quite unknown feature in **LDAP** bind that allows you to issue a _Bind request_ without providing a password. It's equivalent to an _anonymous_ bind, except that the server can log the user's name, thus being able to trace what the user does. Servers might forbid such bind, and this will be the case if the server disallow anonymous binds.
Note that this kind of bind will be supported only if the server allows anonymous binds. It's not supported by *ApacheDS* by default.
```Java
@Test
public void testSimpleBindRequest() throws Exception
{
connection.bind( "uid=admin,ou=system" );
}
```
### SASL Binding
There are various **SASL** mechanisms that can be used to bind to a **LDAP** server. The **Apache LDAP API** support the following 5 mechanisms:
#### PLAIN
A PLAIN SASL bind will authenticate a user by its name and credentials, instead of using a DN.
It's described in [RFC 4616](https://datatracker.ietf.org/doc/html/rfc4616).
When using **Apache Directory Server**, configured to accept such **SASL** mechanism, the user can be configured to search for such users in a specific subtree (default to _ou=users,ou=system_).
The coder looks like that:
```
SaslPlainRequest saslPlainRequest = new SaslPlainRequest();
saslPlainRequest.setAuthorizationId(null);
saslPlainRequest.setUsername( "superuser" );
saslPlainRequest.setCredentials( "test" );
connection.bind( saslPlainRequest );
// At this point, if all goes fine, the user has been authenticated
```
assuming an entry for **superuser** exists:
```
dn: uid=superuser,ou=users,ou=system
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: top
cn: superuser
sn: administrator
displayName: Directory Superuser
uid: superuser
userPassword: test
```
>**NOTE:** The password is stored as plain text in this sample, it does not have to be. It would also work if it were hashed, and it most certainly should be...
What is important is that we need to have an entry which **uid** contains the user's name (here, _superuser_), and this entry is under _ou=users,ou=system_.
It's up to the server you are binding to accept or not such feature.
Now, the right question would be: why would one use **SASL PLAIN** authentication, when it's pretty much like a simple bind? In some ways, it allows an authentication using a simple name, instead of providing a full **DN**.
#### CRAM-MD5
To be completed...
#### DIGEST-MD5
To be completed...
#### EXTERNAL
To be completed...
#### GSSAPI
First, non-trivial **Kerberos** authentication requires configuration. Creating a **Kerberos** configuration is not well documented elsewhere, so we include here sample code. One approach is to create a **JAAS** configuration file. Here’s what such a file might contain:
```Text
myapp {
com.sun.security.auth.module.Krb5LoginModule required debug=true useKeyTab=true principal="host/ilab2.myorg.org@MYORG.ORG" refreshKrb5Config=true keyTab="/etc/krb5.keytab";
};
```
See online documentation for _Krb5LoginModule_ for possible options. This example uses the host’s principal, stored in _/etc/krb5.keytab_. What goes after **@** is the **Kerberos** domain name.
Note that if you specify a credential cache, the cache must be in a file. Many operating systems now put the user’s credentials in **KEYRING** or **KCM**. That worn’t work with Java.
_“myapp”_ is the name supplied as the login context name. See the code below.
When you run the program, you must tell java where the configuration file is. e.g. _“java -Djava.security.auth.login.config=jaas.config”_.
Here is code to connect to ldap with this configuration. We assume that _ldapNetworkConnection_ has already been opened using connect.
```Java
saslGssApiRequest = new SaslGssApiRequest();
saslGssApiRequest.setLoginModuleConfiguration(Configuration.getConfiguration());
saslGssApiRequest.setLoginContextName( "myapp");
saslGssApiRequest.setMutualAuthentication( true );
try {
BindResponse br = ldapNetworkConnection.bind( saslGssApiRequest );
ldapNetworkConnection.startTls();
} catch ( LdapException e ) {
e.printStackTrace();
}
```
At this point you can do operations such as search. Note that the argument to _setLoginContextName_ must match the name in the configuration file. This sample uses mutual authentication. It is possible that some **LDAP** servers might not support that. If so you can set it to false. You may not need _startTls_ if the connection is already secure.
Sometimes it is more convenient to supply the configuration information programmatically. Here is an example that sets the same options as the config file
```Java
class KerberosConfiguration extends Configuration {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
Map<String, String> options = new HashMap<String, String>();
options.put("useKeyTab", "true");
try {
options.put("principal", "host/" + InetAddress.getLocalHost().getCanonicalHostName() + “@MYORG.ORG”);
} catch (Exception e){
System.out.println("Can't find our hostname " + e);
}
options.put("refreshKrb5Config", "true");
options.put("keyTab", "/etc/krb5.keytab");
options.put("debug", "true");
return new AppConfigurationEntry[]{
new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options),};
}
}
```
Here is how it is used:
```Java
saslGssApiRequest = new SaslGssApiRequest();
saslGssApiRequest.setLoginModuleConfiguration(new KerberosConfiguration());
saslGssApiRequest.setMutualAuthentication( true );
try {
BindResponse br = ldapNetworkConnection.bind( saslGssApiRequest );
ldapNetworkConnection.startTls();
} catch ( LdapException e ) {
e.printStackTrace();
}
```
Note that it is not necessary to set the login context name, since it is only needed to process the configuration file.’
Warnings:
* **Apache Kerby** does not implement the option to prompt the user for a password, nor does it permit a password to be passed to **GSSAPI**. Thus authentication is limited to key tables and credential caches.
* The **Apache Kerby** code sets two system properties. In the default case, it clears **KRB5_CONF** and sets _javax.security.auth.useSubjectCredsOnly_ to true. If other code in your application uses **Kerberos**, be aware that there could be conflicts. Be particularly concerned about multi-threaded code that uses different values. Note that in Kirby you can explicitly set **KRB5_CONF**, with _setKrb5ConfFilePath_, or cause Kirby to set it with setRealmName, setKdcHost, and setKdcPort. If you use different settings for these options in different threads, there is a potential thread-safety issue.
* The Java implementation of **Kerberos** has the ability to read standard **Kerberos** credential cache files, e.g. _/tmp/krb5cc_%{uid}_. However it can’t read credentials from **KEYRING** or **KCM**. If your code asks for authentication to come from an existing credential cache, make sure that it is in a file. If you write an application that expects to use existing credentials for logged in users, without prompting for a password, you may need to set _default_ccache_name_ in _/etc/krb5.conf_ to a file, e.g.
_default_ccache_name = /tmp/krb5cc_%{uid}_
This is not an issue if your authentication comes from key tables or prompts the user.
* The encryption types supported depends upon the Java version. E,g. _aes128-cts-hmac-sha256-128_ and _aes256-cts-hmac-sha384-192_ are supported only as of Java 11.
## Rebinding
It's possible to issue a **Bind** on an already bound connection and the existing **LDAP** session will be terminated, and replaced by a new **LDAP** session. In any case, the connection is not dropped when doing so. Note that if the connection was encrypted, it remains encrypted.
```Java
@Test
public void testRebind() throws Exception
{
connection.bind( "uid=admin,ou=system", "secret" );
// Now, rebind
connection.bind( "cn=john doe,dc=example,dc=com", "secret" );
assertTrue( connection.isConnected() );
assertTrue( connection.isAuthenticated() );
}
```
If you issue a _bind_ on the same connection with different credentials, then the existing session will be terminated on the server, and a new one will be created. All the pending requests for the initial session will be lost.
## Unbinding
This is a trivial operation : you just send an **UnbindRequest** to the server, which will invalidate your session.
It's important to know that when you issue an **Unbind**, the connection is dropped. It's done like this:
```Java
@Test
public void testUnbind() throws Exception
{
connection.bind( "uid=admin,ou=system", "secret" );
// Now, unbind
connection.unBind();
assertFalse( connection.isConnected() );
assertFalse( connection.isAuthenticated() );
// And Bind again.
connection.bind( "uid=admin,ou=system", "secret" );
}
```
>**Note** In this example, we have called the _unbind_ operation, which has closed the connection. How is it possible that a _bind_ operation can be applied on a closed connection? Actually, the **LDAP API** will automatically recreate a connection in this case.
Last, but not least, if you close the connection, the session also terminates, without having to call _unbind_.