As we have seen earlier in the book, OpenLDAP supports two different methods of binding (or authenticating) to the directory. The first is to use simple binding. The second is to use SASL binding. In this part we will look at each of these two methods of authentication.
It is not necessary to choose one or the other. OpenLDAP can be configured to do both, at which point it is up to the client as to which method will be used. Simple binding is easier to configure (there is very little configuration that must be done). But SASL is more secure and more flexible, though these benefits come at the cost of added complexity.
The first form of authentication we will look at is simple binding. It is simple not necessarily from the user’s perspective, but it is definitely easier to configure, and the process of binding is easier on the server, too, since less processing is needed.
To perform a simple bind the server requires two pieces of information: a DN and a password. If both the DN and the password fields are empty then the server attempts to bind as the Anonymous user.
During a simple bind the client connects to the server and sends the DN and password information to the server, without adding any additional security. The password, for example, is not specially encrypted.
If the client is communicating over TLS/SSL, then the whole transaction will be encrypted, and so the password will be safe. If the client is not using TLS/SSL then the password will be sent over the network in cleartext. This, of course, is a security issue, and should be avoided (perhaps by using the
security directive discussed in the previous section, or by using an SASL bind instead of a simple bind).
There are two common ways in which client applications attempt to perform a simple bind. The first is sometimes called Fast Bind. In a Fast Bind, the client supplies a full DN (
uid=matt,ou=users,dc=example,dc=com) and also a password (
myPassword). It is faster than the common alternative (binding as anonymous and searching for the desired DN).
Cyrus SASLAuthd, which provides SASL authentication services to other applications, is the application in which the term “Fast Bind” was first used. SASLAuthd is a useful tool for providing SASL authentication services. We will look at it again in the next section. Nowhere in the OpenLDAP documentation, is the term “Fast Bind” used.
The directory first performs, as the anonymous user, an auth access on the
userPassword attribute of the DN that the client supplies. In an auth access the server compares the value of the supplied password to the value of the
userPassword stored in the directory. If the
userPassword value is hashed (with, for example, SSHA or SMD5), then SLAPD hashes the password that the user supplies, and then compares the hashes. If the values match, OpenLDAP binds the user and allows it to perform other LDAP operations.
The OpenLDAP command-line clients, when used with the
-x option, perform simple binding. The clients require that you specify the entire user DN and a password, and they then perform a Fast Bind.
That’s a Fast Bind. But there is a second common method of doing a simple bind—a method designed to eliminate the requirement that the user know an entire DN.
In this second method (which is not, incidentally, called a “slow bind”), the client application requires that the user only know some particular unique identifier—usually the value of
cn. The client application then binds to the server as anonymous (or another pre-configured user) and performs a search for a DN that contains the matching attribute value. If it finds one (and only one) matching DN, then it re-binds, using the retrieved DN and the user-supplied password.
Usually, client applications that use simple bind will need a base DN. The second method of performing a simple bind requires one additional piece of information not required in a Fast Bind: a search filter. The filter is usually something like
(&(uid=?)(objectclass=inetOrgPerson)), where the question mark (
?) is replaced by the user-supplied value.
Using an Authentication User for Simple Binding
While it is more convenient for the user when only a user ID or a CN is required, the second method we have seen may raise an additional concern: the Anonymous user, in order to perform the search, must have read access to all user records in the directory. This means that anyone can connect to the directory (remember, Anonymous has no password) and perform searches.
In many cases this isn’t a problem. Allowing someone to see a list of all the users in the directory may not be a security concern at all. But in other cases, such access would not be acceptable.
One way to work around this problem is to use a different user (rather than Anonymous) to perform the search for the user’s DN. In the last chapter, we created just such an account. Here is the LDIF record we used:
# Special Account for Authentication: dn: uid=authenticate,ou=System,dc=example,dc=com uid: authenticate ou: System description: Special account for authenticating users userPassword: secret objectClass: account objectClass: simpleSecurityObject
The purpose of this account is to log into the server and perform searches for DNs. In other words, it conducts the same job as the Anonymous user, but it adds a little more security, since clients that use the
uid=authenticate account will have to have the appropriate password, too.
To make this clear let’s look at the case where a client, configured to use the Authenticate account, binds a user that identifies himself as
matt with the password
Here’s a step-by-step breakdown of what happens when doing a bind operation this way:
Client connects to the server and starts a bind operation with the DN
uid=autenticate,ou=system,dc=example,dc=comand the password
The server, as Anonymous, compares the Authenticate password,
secret, with the value of the
userPasswordattribute for the
If the above succeeds, then the client (now logged in as the Authenticate user) performs a search with the filter:
uidis unique, the search should return either 0 or 1 record.
If one matching DN is found (in our case, it would be
uid=matt,ou=user,dc=example,dc=com), then the client tries to re-bind as this DN, and using the password the user initially supplies to the client (
The server, as Anonymous, compares the user-supplied password,
myPassword, with the value of the
If the password comparison succeeds then the client application can continue performing LDAP operations as
The process is lengthy and it requires that the client application be configured with bind DN and password information for the Authenticate user, but it adds an additional layer of security to an Anonymous bind and search.
In this section, we have looked at three different ways of performing a simple bind. Each of these methods is useful in particular circumstances, and when used in conjunction with SSL/TLS, simple binding does not pose a significant security threat when the password is transmitted across the network.
Simple Binding Directives in slapd.conf
There are only a few directives in
slapd.conf that have any bearing on simple binding. Simple binding is allowed by default. To prevent SLAPD from accepting simple bind operations, you can use the
require SASL directive which will require that all bind operations are SASL bind operations. Additionally, the
security directive provides the
simple_bind= SSF check, which can be used to require a minimum SSF for simple bind operations. This is covered in more detail in the section entitled The security Directive.
But there are times when it is desirable to have an even more secure authentication process, or when the bind-search-rebind method of simple binding is too much for the client to do. In such cases using SASL binding may be even better.
SASL provides a second method of authenticating to the OpenLDAP directory. SASL works by supplanting the simple bind method outlined above with a more robust authentication process.
The SASL standard is defined in RFC 2222 ( http://www.rfc-editor.org/rfc/rfc2222.txt).
SASL supports a number of different kinds of underlying authentication mechanisms, ranging from login/password combinations to more complex configurations like One-Time Passwords (OTP) and even Kerberos ticket-based authentication.
While SASL provides dozens of different configuration options, we will cover only one. We will configure SASL for doing DIGEST-MD5 authentication. It is slightly more difficult to set up than some SASL mechanisms, but does not require the detailed configuration involved in GSSAPI or Kerberos.
The Cyrus SASL documentation (at
/usr/share/doc/libsasl2 or available online at http://asg.web.cmu.edu/sasl/) provides information on implementing other mechanisms.
In DIGEST-MD5 authentication, the user’s password will be encrypted by the SASL client, sent across the network in its encrypted form only, then decrypted by the server and compared to a cleartext version of the password.
The advantage to using DIGEST-MD5 is that the password is protected when transmitted over the network. The disadvantage, however, is that the passwords must be stored on the server in cleartext.
Contrast this with the way simple bind works. In a simple bind the password itself is not encrypted when crossing the network, but the copy of the password stored in the database is stored in an encrypted format (unless you configure OpenLDAP otherwise).
Keep in mind that when SSL/TLS is used, all data transmitted over the connection is encrypted, including passwords.
Configuring SASL is more complex than configuring simple bind operations. There are two parts to configuring SASL support:
Configuration of Cyrus SASL
Configuration of OpenLDAP
When we installed OpenLDAP in Chapter 2, one of the packages we installed was Cyrus SASL (the library was named
libsasl2). We will also need the SASL command-line tools, which are included in the
$ sudo apt-get install sasl2-bin
Included in this package are the
saslpasswd2 program and the SASL testing client and server applications.
Now we are ready to start configuring.
The SASL Configuration File
The SASL library can be used by numerous applications, and each application can have its own SASL configuration file. SASL configuration files are stored in the
/usr/lib/sasl2 directory. In that directory, we will create a configuration file for OpenLDAP. The file,
slapd.conf, looks like this:
# SASL Configuration pwcheck_method: auxprop sasldb_path: /etc/sasldb2
Do not confuse this
slapd.conf, located at
/usr/lib/sasl2 with the main
slapd.conf file at
/etc/ldap/. These are two different files.
As usual, lines that begin with the
pound sign (#) are comments. The second line determines how SASL will try to check passwords. For example, SASL comes with a stand-alone server, saslauthd, which will handle password checking. In our case though, we want to use the
auxprop plugin, which does the password checking itself, rather than querying the
The last line tells SASL where the password database (which stores a cleartext version of all of the passwords) is located. The standard location for this database is
As we get started, we will store the SASL password in the
/etc/sasldb2 database. To add a password to the database we use the
$ sudo saslpasswd2 -c -u example.com matt
Note that we have to run the above using
sudo because the password file is owned by root. Both
saslpasswd2 will prompt you to enter a password.
-c argument for
saslpasswd2 indicates that we want the user ID to be created if it does not already exist.
-u example.com sets the SASL realm. SASL uses realms as a way to partition the authentication name space. Client applications typically provide SASL with three pieces of information: the username, the password, and the realm. By default, clients will send their domain name as the realm.
Using realms, it is possible to give the same user name different passwords for different applications or application contexts. For example,
matt in realm
example.com can have one password, while
matt in realm
testing.example.com can have a different password.
For our purposes we need only one realm, and we will name it
example.com. When the given command is run it will prompt for a password for user
matt, and then prompt for a password confirmation. If the passwords match, it will store the password in clear text in the SASL password database.
Now we are ready to configure OpenLDAP.
Configuring SLAPD for SASL Support
The OpenLDAP side of SASL configuration is done in the
slapd.conf file for the server, and the
ldap.conf file for the client. In this section, we will focus on the SLAPD server.
When OpenLDAP receives a SASL authentication request it receives four pieces of information from the client. The four fields of information it gets are:
Username: This field contains the ID that the user supplied when authenticating.
Realm: This field contains the SASL realm in which the user is authenticated.
SASL Mechanism: This field indicates which authentication system (mechanism) was used. Given our SASL configuration, this should be DIGEST-MD5.
Authentication Information: This field is always set to
authto indicate that the user needs authentication.
All of this information is compacted into one DN-like string that looks like this:
The order of the fields above is the same as the order of the bulleted list: User-name, realm, SASL mechanism, and authentication information. Note however, that the realm is not required and might not always be present. If SASL does not use any realm information, the realm field will be omitted.
Of course, we do not have any records in our LDAP with DNs like the SASL string above. So, in order to correlate the authenticated SASL user with a user in the LDAP, we need to set up some method of converting the above DN-like string into a DN that is structured like the DNs in the directory. So we want to make the given string into something like this:
There are two ways of doing this mapping. We can either configure a simple string replacement rule to convert the SASL information string to a DN like the last one, or we could perform a search of the directory for an entry with a
uid that is
matt, and then, if a match is found, use that matching entry’s DN.
Each of these two methods has its advantages and disadvantages. Using string replacement is faster, but it is less flexible, and it may not be sufficient for complex directory information trees. Using string replacement it may be necessary to use several
authz-regexp directives in a row, each one with a different regular expression and replacement string.
Searching for the user on the other hand, can be much more flexible in a directory with lots of subtrees. But it will incur the overhead of doing an additional search of the LDAP tree, and it may require tweaking ACLs to allow pre-authentication searches.
Both methods use the same directive in
authz-regexp directive. Let’s look at an example of each method, beginning with the string replacement method.
Using a Replacement String in authz-regexp
authz-regexp directive takes two parameters: a regular expression for getting information out of the SASL DN-like string, and a replacement function (which is different depending on whether we do string replacement or a search).
For our regular expression we want to take the username from the SASL information and map it to the
uid field in a DN. We don’t really need any of the information in the other three SASL fields, so our regular expression is fairly simple:
This rule starts at the beginning of the line (
^) and looks for an entry that starts with
uid=. The next part,
([^,]+), stores characters after
uid= and before a comma (
,) in a special variable called
$1. The rule reads “match as many characters as possible (but at least one character) that are not commas and store them in the first variable (
After that, the rule (using
.* to match anything) skips over the realm (if there is one) and the mechanism, and then looks for a match at the end of the line:
cn=auth$ (where the dollar sign (
$) indicates a line ending).
Once the regular expression is run we should have a variable,
$1, which contains the user’s name. Now we can use that value in a replacement rule, setting the
uid value to the value of
$1. The entire
authz-regexp line looks like this:
authz-regexp "^uid=([^,]+).*,cn=auth$" "uid=$1,ou=Users,dc=example,dc=com"
authz-regexp directive, I have inserted the regular expression we just looked at. After the regular expression comes the replacement rule, which instructs SLAPD to insert the value of
$1 in the
uid field of this template DN.
authz-regexp directive can go anywhere in the
slapd.conf file before the first
authz-regexp is the only necessary directive for configuring SASL, we can now test SLAPD from the command line, without making any additional changes to
$ ldapsearch -LLL -U email@example.com -v '(uid=matt)' uid ldap_initialize( <DEFAULT> ) SASL/DIGEST-MD5 authentication started Please enter your password: SASL username: firstname.lastname@example.org SASL SSF: 128 SASL installing layers filter: (uid=matt) requesting: uid dn: uid=matt,ou=Users,dc=example,dc=com uid: matt
Previously, we have used the
-x flag, combined with
-D, to do a simple bind with a full DN and a password.
With SASL however, we don’t need the full DN. All we need is a shortened connection string. So, instead of using the
-D flags, we just use
-U email@example.com. The
-U flag takes a SASL username and (optionally) a realm. The realm is appended to the username, separated by the at sign (
@). So, in the given example, we are connecting with username
matt and realm
To review, what is happening in the previous command is this:
The client is connecting to SLAPD requesting an SASL bind.
SLAPD uses the SASL subsystem (which checks the
/usr/lib/sasl/slapd.conffile for settings) to tell the client how to authenticate. In this case, it tells the client to use DIGEST-MD5.
The client sends the authentication information to SLAPD.
SLAPD performs the translation specified in
SLAPD then checks the client’s response (using the SASL subsystem) against the information in
When the client authentication succeeds, OpenLDAP runs the search and returns the results to the client.
Now we are ready to look at using
authz-regexp to search the directory with a specific filter.
Using a Search Filter in authz-regexp
In this case, we want to search the directory for an entry that matches the username (
uid) received during the SASL bind. Recall that the SASL authentication information comes in a string that looks like this:
In the last case, we mapped the given directly on to a DN of the form:
But what do we do if we don’t know, for example, if the user
matt is in the Users OU or the System OU? A simple mapping function will not work. We need to search the directory. We will do this by changing the last argument in our
authz-regexp directive looks like this:
authz-regexp "^uid=([^,]+).*,cn=auth$" "ldap:///dc=example,dc=com??sub?(uid=$1)"
This regular expression is the same as the one in the previous example. But the second argument to
authz-regexp is an LDAP URL.
For an overview of writing and using LDAP URLs see Appendix B.
This LDAP URL instructs SLAPD to search in the base
dc=example,dc=com (using a subtree (
sub) search) for an entry whose
uid equals the value of
$1, which gets replaced by the value retrieved from the regular expression in the first argument to
authz-regexp. If the user
matt attempts to authenticate, for example, the URL will look like this:
When SLAPD performs that search against our directory information tree, it will get a single record back—the record with the DN
Here’s an example using
ldapsearch. It is the same example used in the previous section, and it should have the same results even though we are using the LDAP search method:
$ ldapsearch -LLL -U firstname.lastname@example.org -v '(uid=matt)' uid ldap_initialize( <DEFAULT> ) SASL/DIGEST-MD5 authentication started Please enter your password: SASL username: email@example.com SASL SSF: 128 SASL installing layers filter: (uid=matt) requesting: uid dn: uid=matt,ou=Users,dc=example,dc=com uid: matt
A Note on ACLs and Search Filters
When SLAPD reads the search filter, it performs a search of the directory. But the search is done as the Anonymous user. What this means is that we will need to make sure that the Anonymous user will need to have the requisite permissions to search the directory using the filter.
Given our last example, the Anonymous user will need to be able to search the
dc=example,dc=com subtree for
uid values. The ACLs that we created in Chapter 2 do not grant the Anonymous user any such permission. We will need to add one rule to our ACLs in order to allow the search to operate successfully:
access to attrs=uid by anonymous read by users read
This rule, which should appear at the top of the list of ACLs, grants read access to the
uid attribute to
anonymous and to any authenticated users on the system. The important part, in this example, is that Anonymous gets read access.
Keep in mind that by adding this rule, we are making it possible for unauthenticated users to see what user IDs exist in the database. Depending on the nature of your directory data, this may be a security issue. If this is a problem you can either use the string replacement method (remember, you can use several
authz-regexp expressions in a row to handle more complex pattern matching), or you can try to reduce exposure to the
uid field by building more restrictive ACLs
Later in this chapter, we will take a more detailed look at ACLs.
Failure of Mapping
In some cases the mapping done by
authz-regexp will fail. That is, SLAPD will search the directory (using the search filter) and not find any matches. The user, however, is authenticated, and SLAPD will not fail to bind.
Instead, what will happen is that the user will bind as the SASL DN. Thus, the effective DN may be something like:
It makes no difference that there is no actual record in the directory with that username. The client will still be able to access the directory.
But this DN is also subject to ACLs, so you can write access controls targeted at users who have authenticated through SASL but who do not have a DN corresponding to a record in the directory.
Removing the Need to Specify the Realm
In our configuration all of the users are in the same realm,
example.com. Rather than typing that the username and the realm be typed in every time, we can configure a default realm in
slapd.conf by adding the following directive:
If we restart the server with this new modification, we can now run an
ldapsearch without having to specify the realm:
$ ldapsearch -LLL -U matt -v '(uid=matt)' uid ldap_initialize( <DEFAULT> ) SASL/DIGEST-MD5 authentication started Please enter your password: SASL username: matt SASL SSF: 128 SASL installing layers filter: (uid=matt) requesting: uid dn: uid=matt,ou=Users,dc=example,dc=com uid: matt
This time, passing
-U matt was sufficient for authentication. SLAPD automatically inserted the default realm into the SASL information.
Debugging the SASL Configuration
Getting the correct SASL configuration can be frustrating. One way of improving your ability to debug is to configure logging in such a way that you can see what is going on during a SASL transaction. The
trace debugging level (
1) can be used to watch what is happening in SASL. You can either set the debug level in
slapd.conf to trace (or just the digit
1), or you can run
slapd in the foreground on the command line:
$ sudo slapd -d trace # some of the voluminous output removed... slap_sasl_getdn: u:id converted to uid=matt,cn=DIGEST-MD5,cn=auth >>> dnNormalize: <uid=matt,cn=DIGEST-MD5,cn=auth> <<< dnNormalize: <uid=matt,cn=digest-md5,cn=auth> ==>slap_sasl2dn: converting SASL name uid=matt,cn=digest-md5,cn=auth to a DN slap_authz_regexp: converting SASL name uid=matt,cn=digest-md5,cn=auth slap_authz_regexp: converted SASL name to uid=matt,ou=Users,dc=example,dc=com slap_parseURI: parsing uid=matt,ou=Users,dc=example,dc=com ldap_url_parse_ext(uid=matt,ou=Users,dc=example,dc=com) >>> dnNormalize: <uid=matt,ou=Users,dc=example,dc=com> <<< dnNormalize: <uid=matt,ou=users,dc=example,dc=com> <==slap_sasl2dn: Converted SASL name to uid=matt,ou=users,dc=example,dc=com slap_sasl_getdn: dn:id converted to uid=matt,ou=users,dc=example,dc=com
Following this log, we can see the initial SASL string,
uid=matt,cn=DIGEST-MD5,cn=auth, and watch as it is normalized, run through the regular expression, and converted to
ldapwhoami client and the
slapauth utility are also useful when attempting to debug SASL. An example of using
ldapwhoami to evaluate the results of
authz-regexp is given in the next section.
Using Client SSL/TLS Certificates to Authenticate
SASL and SSL/TLS can be used in combination to perform SASL EXTERNAL authentication. In SASL EXTERNAL authentication the SASL module relies upon an external source, in this case a client’s X.509 certificate, as a source of identity.
Using this configuration a client with an appropriately signed certificate can bind to SLAPD without having to enter a username and password, but in a way that is still secure.
How does this work? Just as it is possible to issue a server a certificate for SSL/TLS communication, it is also possible to issue one to a user or client. We have discussed already how a certificate provides, in a secure way, identity information about a server. A client certificate can serve the same purpose.
Authentication, using SASL EXTERNAL works like this:
- The client and server communicate with SSL/TLS protection, either using LDAPS or using StartTLS
- When the server sends its certificate, it requests that the client also provide a certificate
- The client sends its own certificate, which includes the following
- Identity information
- A public key
- The signature of a certificate authority that the server will recognize
- The server, after verifying the certificate, passes the identity information on to SLAPD through the SASL subsystem
- SLAPD then uses that information to bind
Since the certificate sent by the client contains all of the information needed to verify the client’s identity, no login/password combination is needed.
Configuring the SASL EXTERNAL mechanism requires the following steps:
- Create a new client certificate
- Configure the client to send the certificate
- Configure SLAPD to correctly handle client certificates
- Configure SLAPD to correctly translate the identity information provided in the client certificate
Creating a New Client Certificate
Creating a new client certificate is not significantly different from creating a server certificate. We will use the same certificate authority that we created earlier in this chapter.
First, we need to create a new certificate request:
$ /usr/lib/ssl/misc/CA.pl -newreq Generating a 1024 bit RSA private key ............++++++ ..++++++ unable to write 'random state' writing new private key to 'newkey.pem' Enter PEM pass phrase: Verifying - Enter PEM pass phrase: ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:Illinois Locality Name (eg, city) :Chicago Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example.Com Organizational Unit Name (eg, section) : Common Name (eg, YOUR name) :matt Email Address :firstname.lastname@example.org Please enter the following 'extra' attributes to be sent with your certificate request A challenge password : An optional company name : Request is in newreq.pem, private key is in newkey.pem
This process is just like the one before, though the fields are completed specifically for the user who is represented by this certificate. For example, if we were generating this certificate for Barbara, we would complete the Common Name and Email Address fields with her information.
What should go in the Common Name field?
Earlier we used the CN field to store a domain name. What should go in an individual’s CN field? One option is to use the user’s full name. A more pragmatic option is to use an identifier that is used in the user’s LDAP DN (such as the value of the user’s
uid attribute). This makes mapping from a certificate to an LDAP record easier.
Now, we have the new request (
newreq.pem) and key (
newkey.pem). The next thing to do is sign the certificate with our CA’s digital signature:
$ /usr/lib/ssl/misc/CA.pl -signreq Using configuration from /usr/lib/ssl/openssl.cnf Enter pass phrase for ./demoCA/private/cakey.pem: Check that the request matches the signature Signature ok Certificate Details: Serial Number: ba:49:df:f5:8e:7e:77:c6 Validity Not Before: Jul 4 03:28:28 2007 GMT Not After : Jul 3 03:28:28 2008 GMT Subject: countryName = US stateOrProvinceName = Illinois localityName = Chicago organizationName = Example.Com commonName = matt emailAddress = email@example.com X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 9A:97:8F:8C:95:1F:E0:6E:50:BD:DF:F4:C5:71:68:92:3F:A0:30:DD X509v3 Authority Key Identifier: keyid:6B:FB:66:33:5D:DB:32:40:42:D7:71:F7:F0:D0:7C:94:3E:8F:CD:58 Certificate is to be certified until Jul 3 03:28:28 2008 GMT (365 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated unable to write 'random state' Signed certificate is in newcert.pem
Now, we have the signed certificate stored in the file
The next thing to do is to move these files to a location that will be convenient for the user. In this case, we will make a new directory in the user’s home directory and move the files into that directory:
$ sudo mkdir /home/mbutcher/certs $ sudo mv new*.pem /home/mbutcher/certs $ sudo chown -R mbutcher:mbutcher /home/mbutcher/certs
In these three lines, we make a new directory for the certs. In this case, the new
certs/ directory is in the user’s home directory.
Then we move the newly-created certificate files into the new directory. We could rename these files but for now the generic name will suffice.
Finally, we need to make sure that the user has access to his or her certificates. This is done with the
The certificates are ready to use.
Configuring the Client
The next thing we need to do is configure the client to use the certificate and key. This is done by creating
.ldaprc file in the user’s home directory.
A .ldaprc file is a “personal” version of an
ldap.conf file. It supports all of the directives normally included in
ldap.conf, plus a couple of special directives, like the
Since I am the user
mbutcher, I will create this file in my own home directory:
$ cd /home/mbutcher $ touch .ldaprc
Now we can edit the
.ldaprc file. This file needs to indicate that the client is using the SASL EXTERNAL mechanism. Also, it must contain directives about the certificate and key files that we want to use. Additionally, it is not a bad idea to specify the location of the CA certificates (or even to the specific certificate for the CA that signed the server’s certificate), though this is usually done at a global level with the
.ldaprc file then, looks like this:
SASL_MECH EXTERNAL TLS_CERT /home/mbutcher/certs/newcert.pem TLS_KEY /home/mbutcher/certs/newkey.pem TLS_CACERT /etc/ssl/certs/Example.Com-CA.pem
The first directive,
SASL_MECH, indicates what SASL mechanism the client is using. In our case the client is using the
EXTERNAL SASL mechanism.
TLS_CACERT directive points to the specific certificate used for signing the server’s certificate. This will be used by the client libraries to verify the identity of the server during SSL/TLS negotiation.
At this point the client is ready. Now we need to configure SLAPD.
Configuring the Server
SLAPD needs to do a few things in order to make the SASL EXTERNAL mechanism work:
It must request a certificate from the client (otherwise the client will not present one)
It needs to translate the identity information given in the client certificate into a DN that is meaningful in our environment
To set the server to request a client certificate is a matter of adding one directive. In the global section of the
slapd.conf file, before any database directive is specified, the
TLSVerifyClient directive should be added:
TLSCACertificateFile /etc/ssl/certs/Example.Com-CA.pem TLSCertificateFile /etc/ldap/example.com.cert.pem TLSCertificateKeyFile /etc/ldap/example.com.key.pem TLSVerifyClient try
Only the highlighted line is new. The other lines we added earlier in the chapter.
TLSVerifyClient determines whether SLAPD will take steps to request and verify client certificates. There are four possible values:
never: Never request a client certificate. This is the default. If no certificate is requested the client will not provide one. Hence SASL EXTERNAL authentication cannot be used when the
TLSVerifyClientis set to
allow: This will cause SLAPD to request a certificate from the client but if the client does not provide one, or if the provided one is not good (for example if the signature cannot be verified), the session will continue.
try: In this case SLAPD will request a certificate from the client. If the client does not provide a certificate the session will continue. However, if the client provides a certificate that is bad, the session will terminate.
demand: This will cause SLAPD to require a certificate from the client. If the client does not provide one, or if the provided one is not good, the session will terminate.
In the last example we set
try. This simply means that if the client submits a certificate, it must be a valid certificate (with a known CA signature) before SLAPD will allow the connection. But it will also allow clients to connect without supplying a certificate (though such clients will not be able to use SASL EXTERNAL authentication).
At this point, we have SSL/TLS configured correctly. Now, we need to add one additional step: we need to map the identity provided by the certificate (which happens to be a DN) onto a DN for a directory user.
Translating the certificate DN into another DN is not strictly necessary. A user can bind using a certificate DN even if it is not in the directory. ACLs can be written to target such DNs too.
The DN in the client certificate we create looks like this:
Note that this is one long line.
The DN contains the information we entered when running
CA.pl -newreq. What we want to do is translate this DN into the DN of the corresponding LDAP record:
How is this translation done? Using the
authz-regexp directive that we examined earlier in the section on SASL authentication.
There are two fields in the certificate’s identity string that are particularly helpful in identifying the user:
cn. Thus, a simple regular expression can capture these two fields:
This will assign the email address to
$1, and the CN to
From here we could either specify an LDAP URL with a filter for looking up DNs by email address, or we could substitute the CN for the UID field used in the LDAP DN (since the CN maps cleanly onto UID).
We will use this second method, and create
authz-regexp that looks like this:
authz-regexp "^email=([^,]+),cn=([^,]+).*,c=us$" "uid=$2,ou=Users,dc=example,dc=com"
This directive maps the CN value of the certificate DN to the UID attribute in the LDAP authorization DN. Thus, when a client connects with a certificate with the DN
dn:firstname.lastname@example.org,cn=matt,o=example.com,l=chicago,st=illinois,c=us, SLAPD will translate that into the DN
Now we are ready to test things out.
Testing with ldapwhoami
The ideal client for testing this process is
ldapwhoami. This will allow us to connect and bind with SASL EXTERNAL. In addition it will indicate whether or not
authz-regexp mapped the certificate DN to our LDAP DN.
After restarting SLAPD to load the changes, we can test the server:
$ ldapwhoami -ZZ -H 'ldap://example.com' Enter PEM pass phrase: SASL/EXTERNAL authentication started SASL username: emailAddressemail@example.com,CN=Matt, \O=Example.Com,L=Chicago,ST=Illinois,C=US SASL SSF: 0 dn:uid=matt,ou=users,dc=example,dc=com Result: Success (0)
First, let’s take a closer look at the command entered:
ldapwhoami -ZZ -H 'ldap://example.com'
-ZZ flag requires that StartTLS negotiation be done successfully. Using only one
Z will attempt StartTLS, but not close the connect if the negotiations fail. Using
-ZZ is always a good idea when attempting authentication with the SASL EXTERNAL mechanism.
-H 'ldap://example.com' parameter provides the URL of the SLAPD server. Remember that for StartTLS negotiation to work, here, the domain in the LDAP URL must match the domain in the server’s certificate.
What happens when this command is executed? First, the user is prompted for a pass phrase:
Enter PEM pass phrase:
This prompt is actually generated by the SSL/TLS subsystem (OpenSSL). Recall that the key that we generated is protected by a pass phrase. In order to read the key file, the OpenSSL subsystem requires the pass phrase.
But didn’t I say that the SASL EXTERNAL method can obviate the need for entering a password? Yes, it can—but to do so, we would need to remove the passphrase from the key as we did when generating the server certificate:
openssl rsa < newkey.pem > clearkey.pem
Removing the pass phrase may be desirable in some circumstances, and undesirable in others. Keep in mind that removing the pass phrase from the key will make it easier for the certificate to be hijacked by someone else. A key without a pass phrase should be carefully protected by permissions and other means.
Once the user’s pass phrase has been entered, SASL authentication begins:
SASL/EXTERNAL authentication started SASL username: emailAddressfirstname.lastname@example.org,CN=Matt, \O=Example.Com,L=Chicago,ST=Illinois,C=US SASL SSF: 0
As can be seen here, the SASL EXTERNAL mechanism is used, and the SASL username is set to
emailAddressemail@example.com,CN=Matt,O=Example.Com,L=Chicago,ST=Illinois,C=US. Finally, the SASL security strength factor is set to 0 because no SASL security mechanism has been used. Instead, the security mechanisms are external to SASL. Since we are using SSL/TLS with an AES-256 encyrpted certificate, the overall SSF will still be 256.
One important detail to note is that SLAPD will normalize the DN. In normalized form the DN will look like this:
emailAddress attribute has been converted to
authz-regexp that we looked at above operates on this normalized version of the DN.
Finally, the last few lines of output are the results of the LDAP Who Am I? operation:
dn:uid=matt,ou=users,dc=example,dc=com Result: Success (0)
According to SLAPD, the client is currently performing directory operations with an effective DN of
uid=matt,ou=users,dc=example,dc=com. This means that our mapping was successful.
What would the output look like if the
authz-regexp mapping was not successful? It would look something like this:
$ ldapwhoami -ZZ -H 'ldap://example.com' Enter PEM pass phrase: SASL/EXTERNAL authentication started SASL username: emailAddressfirstname.lastname@example.org,CN=Matt,O=Example.Com,L=Chicago, ST=Illinois,C=US SASL SSF: 0 dn:email@example.com,cn=matt,o=example.com,l=chicago,st=illinois,c=us Result: Success (0)
The highlighted portion shows the result of the Who Am I? operation. The DN returned is simply the normalized form of the certificate DN—not the desired LDAP DN.
Going Further with SASL
SASL is a flexible tool for handling authentication. Here we have looked at only two SASL mechanisms: DIGEST-MD5 and EXTERNAL. But there are many other possibilities. It can be used in conjunction with robust network authentication systems like Kerberos. It can take advantage of secure One Time Password systems, like Opiekeys. And it can be used as an interface to more standard password storage systems, like PAM (Pluggable Authentication Modules).
While such configurations are outside of the scope of this book, there are many resources available. The SASL documentation (installed locally on Ubuntu in
/usr/local/doc/libsasl/index.html), and the OpenLDAP Administrator’s Guide (http://openldap.org), both provide more information about different SASL configurations.
Now we will move on from authentication to authorization, and turn our attention to ACLs.