blob: e431491cde0ac282b62947c85366eb53c1ad76a9 [file] [log] [blame]
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
Notice: Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
# 2.2 - Binding and unbinding
In **LDAP**, if one wants to access the data in the base, the common way to do it is to bind to the server. However, it's important to understand that binding is a 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 _Session_ which will hold user information for the duration of the session. This information is limited, but includes the user's credentials.
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 userid/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 with a specific mechanism, like **DIGEST-MD5**, **Kerberos** or certificate based is required.
### Simple Bind
One can issue three kinds of simple binds:
* _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. You either get bound, or will receive an _LdapException_ if an error occurs.
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*.
:::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
To be completed...
#### 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 some 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" );
}
Last, but not least, if you close the connection, the session also terminates.