LDAP Authentication
Prev |  | Next

# 3 Client Configuration

The client should already have OpenLDAP libraries from Section 2.1.3, but if you are installing several client machines you will need to install `net/openldap23-client` on each of them.

FreeBSD requires two ports to be installed to authenticate against an LDAP server, `security/pam_ldap` and `net/nss_ldap`.

## 3.1 Authentication

`security/pam_ldap` is configured via `/usr/local/etc/ldap.conf`.

> Note: This is a different file than the OpenLDAP library functions' configuration file, `/usr/local/etc/openldap/ldap.conf`; however, it takes many of the same options; in fact it is a superset of that file. For the rest of this section, references to `ldap.conf` will mean `/usr/local/etc/ldap.conf`.

Thus, we will want to copy all of our original configuration parameters from `openldap/ldap.conf` to the new `ldap.conf`. Once this is done, we want to tell `security/pam_ldap` what to look for on the directory server.

We are identifying our users with the `uid` attribute. To configure this (though it is the default), set the `pam_login_attribute` directive in `ldap.conf`:

Example 4. Setting `pam_login_attribute`

    pam_login_attribute uid

With this set, `security/pam_ldap` will search the entire LDAP directory under `base` for the value `uid=`username``. If it finds one and only one entry, it will attempt to bind as that user with the password it was given. If it binds correctly, then it will allow access. Otherwise it will fail.

### 3.1.1 PAM

PAM, which stands for “Pluggable Authentication Modules”, is the method by which FreeBSD authenticates most of its sessions. To tell FreeBSD we wish to use an LDAP server, we will have to add a line to the appropriate PAM file.

Most of the time the appropriate PAM file is `/etc/pam.d/sshd`, if you want to use SSH (remember to set the relevant options in `/etc/ssh/sshd_config`, otherwise SSH will not use PAM).

To use PAM for authentication, add the line

    auth  sufficient  /usr/local/lib/pam_ldap.so  no_warn

Exactly where this line shows up in the file and which options appear in the fourth column determine the exact behavior of the authentication mechanism; see pam.d(5)

With this configuration you should be able to authenticate a user against an LDAP directory. PAM will perform a bind with your credentials, and if successful will tell SSH to allow access.

However it is not a good idea to allow every user in the directory into every client machine. With the current configuration, all that a user needs to log into a machine is an LDAP entry. Fortunately there are a few ways to restrict user access.

`ldap.conf` supports a `pam_groupdn` directive; every account that connects to this machine needs to be a member of the group specified here. For example, if you have

    pam_groupdn cn=servername,ou=accessgroups,dc=example,dc=org

in `ldap.conf`, then only members of that group will be able to log in. There are a few things to bear in mind, however.

Members of this group are specified in one or more `memberUid` attributes, and each attribute must have the full distinguished name of the member. So `memberUid: someuser` will not work; it must be:

    memberUid: uid=someuser,ou=people,dc=example,dc=org

Additionally, this directive is not checked in PAM during authentication, it is checked during account management, so you will need a second line in your PAM files under `account`. This will require, in turn, every user to be listed in the group, which is not necessarily what we want. To avoid blocking users that are not in LDAP, you should enable the `ignore_unknown_user` attribute. Finally, you should set the `ignore_authinfo_unavail` option so that you are not locked out of every computer when the LDAP server is unavailable.

Your `pam.d/sshd` might then end up looking like this:

Example 5. Sample `pam.d/sshd`

    auth            required        pam_nologin.so          no_warn
    auth            sufficient      pam_opie.so             no_warn no_fake_prompts
    auth            requisite       pam_opieaccess.so       no_warn allow_local
    auth            sufficient      /usr/local/lib/pam_ldap.so      no_warn
    auth            required        pam_unix.so             no_warn try_first_pass

    account         required        pam_login_access.so
    account         required        /usr/local/lib/pam_ldap.so      no_warn ignore_authinfo_unavail ignore_unknown_user

> Note: Since we are adding these lines specifically to `pam.d/sshd`, this will only have an effect on SSH sessions. LDAP users will be unable to log in at the console. To change this behavior, examine the other files in `/etc/pam.d` and modify them accordingly.

## 3.2 Name Service Switch

NSS is the service that maps attributes to names. So, for example, if a file is owned by user `1001`, an application will query NSS for the name of `1001`, and it might get `bob` or `ted` or whatever the user's name is.

Now that our user information is kept in LDAP, we need to tell NSS to look there when queried.

The `net/nss_ldap` port does this. It uses the same configuration file as `security/pam_ldap`, and should not need any extra parameters once it is installed. Instead, what is left is simply to edit `/etc/nsswitch.conf` to take advantage of the directory. Simply replace the following lines:

    group: compat
    passwd: compat

with

    group: files ldap
    passwd: files ldap

This will allow you to map usernames to UIDs and UIDs to usernames.

Congratulations! You should now have working LDAP authentication.

## 3.3 Caveats

Unfortunately, as of the time this was written FreeBSD did not support changing user passwords with passwd(1). Because of this, most administrators are left to implement a solution themselves. I provide some examples here. Note that if you write your own password change script, there are some security issues you should be made aware of; see Section 4.3

Example 6. Shell script for changing passwords

    #!/bin/sh

    stty -echo
    read -p "Old Password: " oldp; echo
    read -p "New Password: " np1; echo
    read -p "Retype New Password: " np2; echo
    stty echo

    if [ "$np1" != "$np2" ]; then
      echo "Passwords do not match."
      exit 1
    fi

    ldappasswd -D uid="$USER",ou=people,dc=example,dc=org \
      -w "$oldp" \
      -a "$oldp" \
      -s "$np1"

> Caution: This script does hardly any error checking, but more important it is very cavalier about how it stores your passwords. If you do anything like this, at least adjust the `security.bsd.see_other_uids` sysctl value:

>     # sysctl security.bsd.see_other_uids=0.

A more flexible (and probably more secure) approach can be used by writing a custom program, or even a web interface. The following is part of a Ruby library that can change LDAP passwords. It sees use both on the command line, and on the web.

Example 7. Ruby script for changing passwords

    require 'ldap'
    require 'base64'
    require 'digest'
    require 'password' # ruby-password

    ldap_server = "ldap.example.org"
    luser = "uid=#{ENV['USER']},ou=people,dc=example,dc=org"

    # get the new password, check it, and create a salted hash from it
    def get_password
      pwd1 = Password.get("New Password: ")
      pwd2 = Password.get("Retype New Password: ")

      raise if pwd1 != pwd2
      pwd1.check # check password strength

      salt = rand.to_s.gsub(/0\./, '')
      pass = pwd1.to_s
      hash = "{SSHA}"+Base64.encode64(Digest::SHA1.digest("#{pass}#{salt}")+salt).chomp!
      return hash
    end

    oldp = Password.get("Old Password: ")
    newp = get_password

    # We'll just replace it.  That we can bind proves that we either know
    # the old password or are an admin.

    replace = LDAP::Mod.new(LDAP::LDAP_MOD_REPLACE | LDAP::LDAP_MOD_BVALUES,
                            "userPassword",
                            [newp])

    conn = LDAP::SSLConn.new(ldap_server, 389, true)
    conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
    conn.bind(luser, oldp)
    conn.modify(luser, [replace])

Although not guaranteed to be free of security holes (the password is kept in memory, for example) this is cleaner and more flexible than a simple `sh` script.

Prev | Home | Next
Configuring LDAP |  | Security Considerations