2. OpenLDAP

OpenLDAP is an open source implementation of the Lightweight Directory Access Protocol. It will allow us to create a central repository for information about domain users, groups and computers, and make this information available to Samba (and any other LDAP-aware services) for authentication, authorization and management purposes.

2.1 The LDAP protocol

The Lightweight Directory Access Protocol (LDAP) is a networking protocol for accessing X.500-based directory services. A directory is a specialized database optimized for reading, browsing and searching and supports sophisticated filtering capabilities ([OLDAP]).

Similarly to the Unix file system or the Domain Name System, the structure of this database is a hierarchical inverted tree, with the root at the top; for example:

The LDAP database structure

As in the above picture, the topmost levels of the LDAP tree are often arranged based upon domain names, thus allowing for directory services to be located using the Domain Name System.

Each node in the LDAP tree is called an entry and is uniquely identified by its Distinguished Name (DN), which is made up of the name of the entry itself (called the Relative Distinguished Name, RDN, usually derived from some attribute in the entry), comma-concatenated to the names of its parent entries. For instance, the DN of the entry highlighted in the following picture:

Distinguished Name

is made up of the sequence "uid=Danix", "ou=Users", "dc=kernel-panic" and "dc=it", and is therefore written as "uid=Danix,ou=Users,dc=kernel-panic,dc=it" (see [RFC4514] for a full description of the DN format).

An entry consists of a set of attributes; each attribute has a name (or type) and one or more values. The name is typically a mnemonic string, like "dc" for "Domain Component" or "cn" for "Common Name", and determines the syntax of the corresponding value. ObjectClasses define the attribute structure of an LDAP entry, i.e. which attributes must and which may be present in a specific LDAP entry. Both ObjectClasses and Attributes are defined within Schemas.

Though LDAP is a binary protocol, entries can be represented in a human-readable format by using the LDIF format; for example:

dn: uid=danix,ou=Users,dc=kernel-panic,dc=it
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
objectClass: sambaSamAccount
cn: Daniele Mazzocchio
sn: Mazzocchio
givenName: Daniele
uid: Danix
uidNumber: 2000
gidNumber: 513
homeDirectory: /home/danix
loginShell: /bin/ksh
gecos: Daniele Mazzocchio
structuralObjectClass: inetOrgPerson
[ ... ]

LDAP queries can be represented by means of URLs, which allow you to specify the scope of the search and the search query, and to select which attibutes to return. The syntax of an LDAP URL is:

ldap://[host[:port]]/[DN[?[attributes][?[scope][?[filter][?extensions]]]]]

Most of the URL components are optional:

For example, the following URL:

ldap://ldap.kernel-panic.it/uid=Danix,ou=Users,dc=kernel-panic,dc=it

refers to all attributes in a specific user entry, and an URL like:

ldap:///dc=kernel-panic,dc=it?sn?sub?(givenName=Daniele)

refers to the sn (surname) attribute of all entries with a givenName of "Daniele". For further details, please refer to [RFC4516].

2.2 Installation and configuration

Enough with the theory for now, and on to practice! OpenLDAP is available through OpenBSD's packages and ports system (note: unfortunately, the bdb flavor, providing support for the bdb and hdb backends, is marked as broken since OpenBSD 4.3); the following is the list of packages to be installed:

And the installation is over! OpenLDAP configuration files are stored in /etc/openldap. Client-side configuration is contained in the ldap.conf(5) file; below is a sample configuration file:

/etc/openldap/ldap.conf
# URI of the LDAP server to which the LDAP library should connect
URI             ldap://ldap.kernel-panic.it
# The default base DN to use when performing LDAP operations
BASE            dc=kernel-panic,dc=it

# Size limit to use when performing searches
SIZELIMIT       12
# Time limit to use when performing searches
TIMELIMIT       15
# Never derefernce aliases
DEREF           never

The slapd.conf(5) file provides configuration information for the Standalone LDAP Daemon, slapd(8C):

/etc/openldap/slapd.conf
# Include the necessary schema files. core.schema is required by default, the
# other ones are needed for sambaSamAccount. The samba.schema file can be found
# here and must be copied in /etc/openldap/schema/.
include         /etc/openldap/schema/core.schema
include         /etc/openldap/schema/cosine.schema
include         /etc/openldap/schema/inetorgperson.schema
include         /etc/openldap/schema/nis.schema
include         /etc/openldap/schema/samba.schema

# Absolute path to the PID file
pidfile         /var/run/openldap/slapd.pid
# Absolute path to the file that will hold slapd's command line options
argsfile        /var/run/openldap/slapd.args

# Type of database backend
database        ldbm
# DN suffix of queries that will be passed to this backend database
suffix          "dc=kernel-panic,dc=it"
# Database directory
directory       /var/openldap-data

# The Distinguished Name of the administrator of this database
rootdn          "cn=Manager,dc=kernel-panic,dc=it"
# Password (or password hash) for the rootdn. Clear-text passwords are allowed
# but strongly discouraged; the password hash can be generated using the
# slappasswd(8C) command; e.g.:
# # slappasswd
# New password: <password>
# Re-enter new password: <password>
# {SSHA}d1bjQZEA43NFKNL7g48XjaNv/W6DG0fY
rootpw          {SSHA}d1bjQZEA43NFKNL7g48XjaNv/W6DG0fY

# Maintain indices on the most useful attributes to speed up searches made on
# the sambaSamAccount, posixAccount and posixGroup objectClasses
index   objectClass             eq
index   cn                      pres,sub,eq
index   sn                      pres,sub,eq
index   uid                     pres,sub,eq
index   displayName             pres,sub,eq
index   uidNumber               eq
index   gidNumber               eq
index   memberUid               eq
index   sambaSID                eq
index   sambaPrimaryGroupSID    eq
index   sambaDomainName         eq
index   default                 sub

# Access control configuration. The rootdn can always read and write everything
access to attrs=userpassword,sambaLMPassword,sambaNTPassword
    by anonymous auth
    by self write
    by * none

access to *
    by self write
    by * read

We can use the slaptest(8C) command to check the validity of our slapd.conf(5) file:

# install -d -o _openldap /var/run/openldap
# slaptest -u
config file testing succeeded
#

The slapd.conf(5) file, containing the rootpw password, should have restrictive permissions:

# chgrp _openldap /etc/openldap/slapd.conf
# chmod 640 /etc/openldap/slapd.conf

Ok, now everything should be ready for starting slapd(8C). The first time you may want to invoke it with the "-d" option to turn on debugging and keep the daemon in the foreground, to immediately notice any error:

# /usr/local/libexec/slapd -4 -d 256 -u _openldap -g _openldap
[ ... ]
slapd starting

You can check that everything is running correctly by issuing the ldapsearch(1) command:

# ldapsearch -x -b '' -s base '(objectclass=*)' namingContexts
# extended LDIF
#
# LDAPv3
# base <> with scope baseObject
# filter: (objectclass=*)
# requesting: namingContexts
#

#
dn:
namingContexts: dc=kernel-panic,dc=it

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1
#

If everything is working fine, we can configure the system to start slapd(8C) on boot, by adding the following line (containing the command line arguments) to /etc/rc.conf.local(8):

/etc/rc.conf.local
slapd_flags="-4 -u _openldap -g _openldap"

and the following commands to /etc/rc.local(8):

/etc/rc.local
if [ "$slapd_flags" != "NO" -a -x /usr/local/libexec/slapd ]; then
    echo -n ' slapd'
    install -d -o _openldap /var/run/openldap
    /usr/local/libexec/slapd $slapd_flags
fi

2.3 LDAP over TLS/SSL

OpenLDAP comes with built-in support for the TLS/SSL protocols to provide integrity and confidentiality to LDAP connections by means of public-key cryptography. Enabling TLS/SSL will prevent traffic from traveling in the clear over the network, thus protecting users' passwords from eavesdroppers.

2.3.1 Setting up the PKI

TLS relies on public key certificates for authentication and therefore requires that you first set up a basic Public Key Infrastructure (PKI) for managing digital certificates. As a preliminary step, we will create the directories where certificates will be stored:

# install -m 700 -d /etc/openldap/ssl/private

The first step in setting up the PKI is the creation of the root CA certificate (/etc/openldap/ssl/ca.crt) and private key (/etc/ssl/private/ca.key) using openssl(1):

# openssl req -days 3650 -nodes -new -x509 -keyout /etc/ssl/private/ca.key \
> -out /etc/openldap/ssl/ca.crt
[ ... ]
Country Name (2 letter code) []: IT
State or Province Name (full name) []: Italy
Locality Name (eg, city) []: Milan
Organization Name (eg, company) []: Kernel Panic Inc.
Organizational Unit Name (eg, section) []: LDAP CA
Common Name (eg, fully qualified host name) []: ca.lan.kernel-panic.it
Email Address []: <enter>
#

The next step is the creation of the private key (/etc/openldap/ssl/private/server.key) and Certificate Signing Request (/etc/openldap/ssl/private/server.csr) for the server:

# openssl req -days 3650 -nodes -new -keyout /etc/openldap/ssl/private/server.key \
> -out /etc/openldap/ssl/private/server.csr
[ ... ]
Country Name (2 letter code) []: IT
State or Province Name (full name) []: Italy
Locality Name (eg, city) []: Milan
Organization Name (eg, company) []: KP Inc.
Organizational Unit Name (eg, section) []: LDAP Server
Common Name (eg, fully qualified host name) []: ldap.kernel-panic.it
Email Address []: <enter>

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []: <enter>
An optional company name []: <enter>
#

Finally, the CA will generate the signed certificate out of the certificate request:

# openssl x509 -req -days 3650 -in /etc/openldap/ssl/private/server.csr \
> -out /etc/openldap/ssl/server.crt -CA /etc/openldap/ssl/ca.crt \
> -CAkey /etc/ssl/private/ca.key -CAcreateserial
Signature ok
subject=/C=IT/ST=Italy/L=Milan/O=Kernel Panic Inc./OU=LDAP Server/CN=ldap.kernel-panic.it
Getting CA Private Key
#

You can generate the client certificate by repeating the last two steps:

# openssl req -days 3650 -nodes -new -keyout /etc/openldap/ssl/private/client.key \
> -out /etc/openldap/ssl/private/client.csr
[ ... ]
Country Name (2 letter code) []: IT
State or Province Name (full name) []: Italy
Locality Name (eg, city) []: Milan
Organization Name (eg, company) []: KP Inc.
Organizational Unit Name (eg, section) []: LDAP Client
Common Name (eg, fully qualified host name) []: ldap.kernel-panic.it
Email Address []: <enter>

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []: <enter>
An optional company name []: <enter>
# openssl x509 -req -days 3650 -in /etc/openldap/ssl/private/client.csr \
> -out /etc/openldap/ssl/client.crt -CA /etc/openldap/ssl/ca.crt \
> -CAkey /etc/ssl/private/ca.key
Signature ok
subject=/C=IT/ST=Italy/L=Milan/O=Kernel Panic Inc./OU=LDAP Client/CN=ldap.kernel-panic.it
Getting CA Private Key
#

As a finishing touch, we need to assign restrictive permissions to the private keys, in order to prevent unauthorized access:

# chown -R _openldap:_openldap /etc/openldap/ssl/private
# chmod 600 /etc/openldap/ssl/private/*

2.3.2 OpenLDAP configuration

Configuring the slapd(8C) daemon for TLS operation simply requires that you add a few lines to slapd.conf(5), right after the rootpw parameter, containing the cipher suites to accept and the paths to the certificates:

/etc/openldap/slapd.conf
# TLS configuration
TLSCipherSuite         HIGH:MEDIUM:+SSLv3
TLSCACertificateFile   /etc/openldap/ssl/ca.crt
TLSCertificateFile     /etc/openldap/ssl/server.crt
TLSCertificateKeyFile  /etc/openldap/ssl/private/server.key

In the client configuration file, ldap.conf(5), you have to change the URI scheme to "ldaps" and specify the path to the CA certificate and the acceptable cipher suites:

/etc/openldap/ldap.conf
[ ... ]
URI              ldaps://ldap.kernel-panic.it

# TLS configuration
TLS_CACERT       /etc/openldap/ssl/ca.crt
TLS_CIPHER_SUITE HIGH:MEDIUM:+SSLv3

As a final step, add the "-h ldaps:///" option to the slapd(8C) command line arguments to make the daemon listen only for LDAP over TLS on TCP port 636:

/etc/rc.conf.local
# Only listen for LDAP over TLS (port 636)
slapd_flags="-4 -u _openldap -g _openldap -h ldaps:///"

and restart slapd(8C).