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/openldap24-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.
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:
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.
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.
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.
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.