Authenticating Users to the Directory

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.

Simple Binding

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).

Note

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.

Note

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 uid or 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 myPassword.

Here’s a step-by-step breakdown of what happens when doing a bind operation this way:

  1. Client connects to the server and starts a bind operation with the DN uid=autenticate,ou=system,dc=example,dc=com and the password secret.

  2. The server, as Anonymous, compares the Authenticate password, secret, with the value of the userPassword attribute for the uid=autenticate,ou=system,dc=example,dc=com record.

  3. If the above succeeds, then the client (now logged in as the Authenticate user) performs a search with the filter: (&(uid=matt)(objectclass=inetOrgPerson)). Since uid is unique, the search should return either 0 or 1 record.

  4. 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 (myPassword).

  5. The server, as Anonymous, compares the user-supplied password, myPassword, with the value of the userPassword attribute of uid=matt,ou=user,dc=example,dc=com.

  6. If the password comparison succeeds then the client application can continue performing LDAP operations as uid=matt,ou=user,dc=example,dc=com.

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.

Tip

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.

Later in this book we will examine several third party applications that use simple binding when connecting to the directory.

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 Binding

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.

Note

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.

Later in this chapter, we will integrate our SASL work with our SSL/TLS work, and use the SASL EXTERNAL mechanism for authenticating to the directory with client SSL certificates.

Note

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

Configuring Cyrus SASL

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 sasl2-bin package:

  $ 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

Note

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 saslauthd server.

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 /etc/sasldb2.

Setting a User Password

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 saslpasswd2 program:

  $ 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 sudo and saslpasswd2 will prompt you to enter a password.

The -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 auth to indicate that the user needs authentication.

All of this information is compacted into one DN-like string that looks like this:

uid=matt,cn=example.com,cn=DIGEST-MD5,cn=auth

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:

uid=matt,ou=Users,dc=example,dc=com

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 slapd.conf: the 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

The 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:

"^uid=([^,]+).*,cn=auth$"

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 ($1).”

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"

After the 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.

The authz-regexp directive can go anywhere in the slapd.conf file before the first database directive.

Since 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 slapd.conf:

$ ldapsearch -LLL -U matt@example.com -v '(uid=matt)' uid
ldap_initialize( <DEFAULT> )
SASL/DIGEST-MD5 authentication started
Please enter your password: 
SASL username: matt@example.com
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 -W and -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 -x, -W, and -D flags, we just use -U matt@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 example.com.

Next, ldapsearch prompts for a password (see the highlighted line in the example). This is not our LDAP password, but our SASL password—the one in the account we created when we ran saslpasswd2.

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.conf file 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 authz-regexp.

  • SLAPD then checks the client’s response (using the SASL subsystem) against the information in /etc/sasldb2.

  • 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:

uid=matt,cn=example.com,cn=DIGEST-MD5,cn=auth

In the last case, we mapped the given directly on to a DN of the form:

uid=<username>,ou=users,dc=example,dc=com.

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.

Our new 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.

Note

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:

ldap:///dc=example,dc=com??sub?(uid=matt)

When SLAPD performs that search against our directory information tree, it will get a single record back—the record with the DN uid=matt,ou=Users,dc=example,dc=com.

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 matt@example.com -v '(uid=matt)' uid
ldap_initialize( <DEFAULT> )
SASL/DIGEST-MD5 authentication started
Please enter your password: 
SASL username: matt@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:

uid=matt,cn=example.com,cn=digest-md5,cn=auth

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:

sasl-realm  example.com

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 uid=matt,ou=users,dc=example,dc=com.

The 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:

  1. Create a new client certificate
  2. Configure the client to send the certificate
  3. Configure SLAPD to correctly handle client certificates
  4. 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 []:matt@example.com

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.

Tip

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              = matt@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 newcert.pem.

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 chown command.

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.

Note

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 TLS_CERT and TLS_KEY directives.

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 ldap.conf file.

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.

The TLS_CERT directive points to the location of the client’s signed X.509 certificate, and the TLS_KEY directive indicates the location of the client’s private key file.

The 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 TLSVerifyClient is set to never.

  • 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 TLSVerifyClient to 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).

If we wanted to force clients to provide a certificate then we would use the demand keyword instead of try.

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.

Note

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:

dn:email=matt@example.com,cn=matt,o=example.com,l=chicago,\
    st=illinois,c=us

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: uid=matt,ou=users,dc=example,dc=com.

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: email and cn. Thus, a simple regular expression can capture these two fields:

^email=([^,]+),cn=([^,]+).*,c=us$

This will assign the email address to $1, and the CN to $2.

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:email=matt@example.com,cn=matt,o=example.com,l=chicago,st=illinois,c=us, SLAPD will translate that into the DN uid=matt,ou=users,dc=example,dc=com.

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: emailAddress=matt@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'

The -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.

Next, the -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

Then the TLS_KEY directive in .ldaprc would need to be adjusted to point to the clearkey.pem file.

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: emailAddress=matt@example.com,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 emailAddress=matt@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:

email=matt@example.com,cn=matt,o=example.com,l=chicago,st=illinois,\
    c=us

The emailAddress attribute has been converted to email, and all uppercase strings have been converted to lowercase. The 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: 
emailAddress=matt@example.com,CN=Matt,O=Example.Com,L=Chicago,
    ST=Illinois,C=US
SASL SSF: 0
dn:email=matt@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.

Related Articles

How to add swap space on Ubuntu 21.04 Operating System

How to add swap space on Ubuntu 21.04 Operating System

The swap space is a unique space on the disk that is used by the system when Physical RAM is full. When a Linux machine runout the RAM it use swap space to move inactive pages from RAM. Swap space can be created into Linux system in two ways, one we can create a...

read more

Lorem ipsum dolor sit amet consectetur

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

17 − 14 =