Commit 562a6ec6 authored by Robert Dubner's avatar Robert Dubner
Browse files

ITS#9717 Add overlay RADIUSOV to contrib/slapd-modules

parent 7c20ca92
# $OpenLDAP$
# This work is part of OpenLDAP Software <http://www.openldap.org/>.
#
# Copyright 2008-2021 The OpenLDAP Foundation.
# Portions Copyright 2008 Howard Chu, Symas Corp. All Rights Reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted only as authorized by the OpenLDAP
# Public License.
#
# A copy of this license is available in the file LICENSE in the
# top-level directory of the distribution or, alternatively, at
# <http://www.OpenLDAP.org/license.html>.
# Path to the OpenLDAP source tree
LDAP_SRC=../../..
# Path to the OpenLDAP object tree - same as above unless
# you're doing out-of-tree builds.
LDAP_BUILD=$(LDAP_SRC)
LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd -I$(LDAP_SRC)/libraries/liblber
LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
$(LDAP_BUILD)/libraries/liblber/liblber.la
NLDAPD_INC=-Iradius-pam-ldapd
LIBTOOL = $(LDAP_BUILD)/libtool
INSTALL = /usr/bin/install
#OPT = -g -O2 -Wall
OPT = -ggdb -O0 -Wall -fmax-errors=10
DEFS =
INCS = $(LDAP_INC) $(NLDAPD_INC)
LIBS = $(LDAP_LIB)
prefix=/usr/local
exec_prefix=$(prefix)
ldap_subdir=/openldap
libdir=$(exec_prefix)/lib
libexecdir=$(exec_prefix)/libexec
moduledir = $(libexecdir)$(ldap_subdir)
sysconfdir = $(prefix)/etc$(ldap_subdir)
schemadir = $(sysconfdir)/schema
mandir = $(exec_prefix)/share/man
man5dir = $(mandir)/man5
all: radiusov.la simple.pem
OBJS = radiusov.lo radius.lo md5.lo hmacmd5.lo rpacket.lo reap.lo tls.lo md4.lo mschap.lo sha1.lo des.lo
.SUFFIXES: .c .o .lo
.c.lo:
$(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $<
$(OBJS): radiusov.h rpacket.h reap.h tls.h md4.h mschap.h sha1.h
radiusov.la: $(OBJS) $(XOBJS)
$(LIBTOOL) --mode=link $(CC) $(OPT) -version-info 0:0:0 \
-rpath $(moduledir) -module -o $@ $(OBJS) $(XOBJS) $(LIBS)
install: all install-lib FORCE
install-lib: radiusov.la
mkdir -p $(DESTDIR)$(moduledir)
$(LIBTOOL) --mode=install cp radiusov.la $(DESTDIR)$(moduledir)
mkdir -p $(sysconfdir)
cp simple.pem $(sysconfdir)
FORCE:
simple.pem:
openssl req -nodes -x509 -newkey rsa:2048 -out simple.pem -keyout simple.pem -days 1 -subj '/CN=localhost'
clean:
rm -f *.*o *.la .libs/*
rm -rf .libs
rm *.pem
# The RADIUSOV overlay for OpenLDAP
The RADIUSOV overlay allows the OpenLDAP `slapd` daemon to provide 802.1x authentication services via the RADIUS protocol.
## The RADIUS protocol?
It's never easy, figuring out what information to include in a README. Assuming the reader knows more than they do leads to frustration. But including too much information obfuscates what the user probably really needs to know.
So, first, a very quick summary:
The RADIUS protocol has been under active development since about 1989, first at the University of Michigan. That software base was taken over by the FreeRADIUS project in the late 1990s, and has been under active development ever since.
A very common use case, to the tune of hundreds of millions of installations, are Enterprise WiFi Access Points (also known as base stations).
In contrast to a typical home or small business, which has a single password for the WiFi network, each user in a large organization has their own unique WiFi password. When such a user connects to an Access Point, the user is asked for their username and password.
The AP then sends an authentication request to the well-known RADIUS UDP port 1812 on a designated RADIUS server. The server's IP address has to be configured into the AP, along with a RADIUS protocol "shared secret" known to both the AP and the server.
The RADIUS server looks up the username's password in a backend database, and the AP and the server go through the protocol and the AP is told that the password is good or bad.
That's enough information to understand the following instructions for deploying and configuring the RADIUSOV overlay. It is highly recommended that users who want a deeper understanding of RADIUS install FreeRADIUS and go through their documentation, which we are not going to attempt to duplicate here.
## Before deployment, compilation
Navigate to `<repositories>/openldap/contrib/slapd-modules/radiusov` and execute
make && make install
Pretty much the only thing that might need changing is the `OPT=` line of the Makefile; right now it's set to the `-O0` optimization level, and you might want something else. In testing, the optimization level made no obvious difference; overall processing time is, I speculate, swamped by communications latencies.
## Loading and configuring the overlay.
Loading and configuring the overlay is primarily controlled by entries in the OpenLDAP `slapd.conf` file. By default, it is found (at least sometimes!) in `/usr/local/etc/openldap` See the [OpenLDAP Administrator's Guide](https://www.openldap.org/doc/) for more information.
### Causing the overlay to load
These entries in `slapd.conf` will load and configure the overlay:
```
# Load the mdb backend
moduleload back_mdb.la
moduleload radiusov.so # Add this to the list of "moduleload" entries
```
The RADIUSOV overlay understands these `slapd.conf` parameters, which get associated with the backend that contains the passwords for the usernames:
```
radiusPort
radiusHost
radiusClientURI
radiusUserURI
radiusTlsConfigFile
```
I don't know about you, but I find examples to be the richest source of answers to the question, "What in tarnation is going on here!" Please look at the Makefile and related configuration files in `./demonstration/ldap`. In particular, the relevant section of `slapd.conf` in the demonstration looks like this:
```
database mdb
suffix "dc=renbud,dc=com"
rootdn "cn=Manager,dc=renbud,dc=com"
rootpw secret
directory ./testdir/openldap-data
maxsize 33554432
overlay radiusov
radiusPort 18129
radiusHost 0.0.0.0
radiusTlsConfigFile ./radiustls.conf
radiusClientURI ldap:///device=RadiusClient,dc=renbud,dc=com?sharedSecret?sub?(&(ipa=$1)(port=$2))
radiusUserURI ldap:///ou=People,dc=renbud,dc=com?userPassword?sub?(uid=$1)
```
radiusPort is set to `18129` so that RADIUSOV and FreeRADIUS can exist concurrently; FreeRADIUS listens for RADIUS packets on the well-known UDP port `1812`, while OpenLDAP will look for RADIUS packets on UDP port `18129`. This is a demonstration configuration; in ordinary operation, FreeRADIUS and OpenLDAP will not run concurrently, and OpenLDAP will listen on `1812`.
radiusHost is set to `0.0.0.0`, so that OpenLDAP will accept packets from anywhere.
radiusClientURI describes how the RADIUS `sharedSecret` for a particular IP address (the `ipa`) is found in the `dc=renbud,dc=com` database.
radiusUserURI describes how the `password` for a particular username (the `uid`) is found in that same database.
With that description as the starting point, I think the most understanding will come the most quickly from examining the working example in `./demonstration/ldap`
## Certificate difficulties
The most likely cause of problems will come when efforts are made to use certificates for authenticating both ends of the TLS link set up by the RADIUS protocol.
The included demonstration shows how to set up radiusclient, OpenLDAP, and FreeRADIUS to do the most rigorous authentication: The servers *demand* that the client send a client certificate, and the servers and the client all check the certificates, and the specified Certificate Authority, for validity and unexpired status.
But setting up a TLS tunnel doesn't have to be that persnickety.
It is necessary for the server to send the client a certificate -- it's needed for the initial handshake that results in a session key for encrypting data through the tunnel -- it is not necessary that that the certificate be current, or that it be signed by a Certificate Authority.
This is controlled, in both the `radiusclient` and RADIUSOV, by the `verify_mode` parameter, which has these four possible values for the server.
```
never - The server doesn't ask the client for a certificate.
allow - The server asks the client for a certificate, but ignores whether or not a certificate is sent, and ignores the certificate if it is sent.
try - The server asks the client for a certificate. If no certificate is sent, processing continues with no error indication. If a certificate is sent, it is checked for validity and an error issued if the certificate is invalid.
demand - The server requires that the client send a valid certificate.
```
On the client side, `never` and `demand` are effectively the valid possibilities.
In the interests of not throwing an error by default, the OpenLDAP RADIUSOV out-of-the-box configuration points to a TLS configuration `radiustls.conf` file that itself points to `/usr/local/etc/openldap/simple.pem`.
That `simple.pem` is a self-signed expired certificate with an unprotected private key. It is completely insecure and utterly unsuitable for *any* purpose except getting RADIUSOV working.
By default, and for the same reasonse, RADIUSOV sets `verify_mode` to `none`.
# OpenLDAP RADIUSOV demonstation
The included ./radiusclient and ./ldap directories demonstrate how the home-grown `radiusclient` application is able to authenticate users by sending RADIUS packets to FreeRADIUS on UDP port 1812, and to OpenLDAP's RADIUSOV overlay on UDP port 18129.
FreeRADIUS isn't necessary, but if you don't want it involved, you'll need to edit it out of the `./radiusclient\Makefile.` Specifically, comment out these lines:
./radiusclient -c $(CLIENT_CONF) -h 127.0.0.1 -p 1812 -m peap -s testing123 testing password
./radiusclient -c $(CLIENT_CONF) -h 127.0.0.1 -p 1812 -m ttls -s testing123 testing password
On my system, I did a FreeRADIUS build from source, which puts configuration files at `/usr/local/etc/raddb`. The various configuration files here that need certificates all use the certificates at `/usr/local/etc/raddb/certs`. To make sure there are valid certificates there, the `./radiusclient` Makefile generates a compatible set of certificates and creates, or overwrites (as necessary), the certificates at `/usr/local/etc/raddb/certs`.
That said: to duplicate my system:
1) Build and install FreeRADIUS from source
2) Build and install OpenLDAP from source.
For production:
./configure --enable-backends=mod --enable-overlays=mod --enable-modules --enable-dynamic --disable-ndb --disable-sql --disable-perl --disable-wt
For a debug build:
CFLAGS="-ggdb -O0" CXXFLAGS="-ggdb -O0" ./configure --enable-backends=mod --enable-overlays=mod --enable-modules --enable-dynamic --disable-ndb --disable-sql --disable-perl --disable-wt
3) Navigate to `openldap/contrib/slapd-modules/radiusov` and execute
make && make install
4) Navigate to `./radiusclient` and execute
make
You need to do this before launching `slapd` or `radiusd` to make sure the correct certificates are in place.
5) Edit the `SLAPD` variable in ./slapd/Makefile. I mostly have been working with a debug build of `slapd`, hence the specific path to `slapd`.
7) Open a terminal window and navigate to `./ldap`. Execute
make scratch
You have to do this once to create a purely local OpenLDAP directory that will be used for the demonstration. Note further that the demonstration uses its own local `slapd.conf` file -- if I did everything right, the demonstration doesn't require any default information, nor will it affect in any way, an OpenLDAP installation on the same computer. It even uses its own TCP/IP port -- port 3899 -- for the demo.
6) Open a terminal window and navigate to `./ldap`. Execute
make both
If you have edited `./radiusclient/Makefile` to eliminate the two `rad:` instructions that reference port 1812, then just execute
make slapd
7) In another terminal window, navigate to `./radiusclient` and execute
make rad
In a perfect world, the process will result in SUCCEEDED indications.
The world being what it is, well, hopefully the error messages in the two terminal windows will give you enough information to figure out what's wrong.
Otherwise, well, give me a yell at support@symas.com
Bob Dubner, October 2021
!Makefile
*.log
schema/
sch/
schema
testdir
*.txt
cscope.out
*.pcapng
beap.scr
SLAPD = ~/repos/openldap/servers/slapd/.libs/slapd
DEBUG_LEVEL=STATS
DEBUG_LEVEL=-1
DEBUG_LEVEL= 4083 # Everything but LDAP_DEBUG_CONNS (8) and LDAP_DEBUG_ARGS(4)
DEBUG_LEVEL=0
DEBUG_LEVEL= 4087 # Everything but LDAP_DEBUG_CONNS (8); it cuts down on noise during debugging
all:
@echo "'make scratch' to build up the LDAP DIT from scratch"
@echo "'make slapd' to launch the OpenLDAP slapd daemon"
@echo "'make radiusd' to launch the FreeRADIUS radiusd daemon"
@echo "'make kill' to kill both slapd and radiusd"
@echo "'make slapdd' for GDB-launched version of the daemons"
@echo "'make radiusdd' for GDB-launched version of the daemons"
scratch:
# Make sure slapd isn't running
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
# Clear out the log files
rm -f slapd.log
rm -f debug.log
# Clear out the database directory completely:
rm -fr testdir
# Create the database directory:
mkdir -p testdir/openldap-data
# Launch SLAPD
LDAPNOINIT=1 /usr/local/libexec/slapd -h "ldap://127.0.0.1:3899/" -s 0 -f slapd.conf -d $(DEBUG_LEVEL) > slapd.log 2>&1 &
@echo "Waiting 1 second..."
sleep 1
# Do the initial database population:
ldapadd -H ldap://127.0.0.1:3899/ -x -D "cn=Manager,dc=renbud,dc=com" -w secret -f renbud.ldif
@echo "slapd is running; use 'make kill' to shut it and the radiusd daemon down"
slapd:
# Make sure slapd isn't running
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
# Clear out the log files
rm -f slapd.log
rm -f debug.log
# Launch SLAPD
# LDAPNOINIT=1 sudo chrt -rr 49 /usr/local/libexec/slapd -h "ldap://127.0.0.1:3899/" -s 0 -f slapd.conf -d $(DEBUG_LEVEL) 2>&1 | tee slapd.log &
LDAPNOINIT=1 $(SLAPD) -h "ldap://127.0.0.1:3899/" -s 0 -f slapd.conf -d $(DEBUG_LEVEL) 2>&1 | tee slapd.log &
slapdd:
# Make sure slapd isn't running
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
# Clear out the log files
sudo rm -f testdir/slapd.pid
sudo rm -f testdir/slapd.args
rm -f slapd.log
rm -f debug.log
# Launch SLAPD with the debugger:
LDAPNOINIT=1 gdb --args $(SLAPD) -h "ldap://127.0.0.1:3899/" -s 0 -f ./slapd.conf -d -1
nc:
echo "abel1,abel1" | nc -w 1 -4u localhost 18129
echo "abel1,badpassword" | nc -w 1 -4u localhost 18129
whoami:
ldapwhoami -Hldap://127.0.0.1:3899/ -x -D "uid=abel1,ou=People,dc=renbud,dc=com" -w abel1
search:
ldapsearch -LLL -Hldap://127.0.0.1:3899/ -x -b "dc=renbud,dc=com" '(uid=abe1)' sn cn
searchd:
gdb --args /home/bob/repos/openldap/clients/tools/.libs/ldapsearch -LLL -Hldap://127.0.0.1:3899/ -x -b "dc=renbud,dc=com" '(uid=abe1)' sn cn
kill:
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
sudo pkill -KILL slapd || true
sudo pkill -KILL radiusd || true
sudo pkill -KILL radiusd || true
sudo pkill -KILL radiusd || true
sudo pkill -KILL radiusd || true
radiusd:
sudo pkill -KILL radiusd || true
sudo pkill -KILL radiusd || true
sudo pkill -KILL radiusd || true
sudo pkill -KILL radiusd || true
rm -f radiust.log
cp eap_for_demo /usr/local/etc/raddb/mods-available
rm -f /usr/local/etc/raddb/mods-enabled/eap
ln -s ../mods-available/eap_for_demo /usr/local/etc/raddb/mods-enabled/eap
$(MAKE) -C radius-config
radiusd -X 2>&1 | tee radiusd.log &
radiusdd:
sudo pkill -KILL radiusd || true
sudo pkill -KILL radiusd || true
sudo pkill -KILL radiusd || true
sudo pkill -KILL radiusd || true
rm -f radiust.log
cp eap_for_demo /usr/local/etc/raddb/mods-available
rm -f /usr/local/etc/raddb/mods-enabled/eap
ln -s ../mods-available/eap_for_demo /usr/local/etc/raddb/mods-enabled/eap
$(MAKE) -C radius-config
gdb --args radiusd -sfxx -l stdout
.PHONY: both
both: radiusd slapd
radius:
rm -f radiust.log
sudo chrt -rr 49 radiusd
rtest:
radtest testing password 127.0.0.1 0 testing123
# radtest abel1 abel1 127.0.0.1 0 testing123
dump-config:
ldapsearch -H ldap://127.0.0.1:3899/ -x -D cn=config -w VerySecret -b cn=config
This directory can create and launch a purely local OpenLDAP instance that doesn't use any OpenLDAP global or default configuration files or data locations.
You'll need to run `make scratch` once to create the Directory Tree.
The base domain is "dc=renbud,dc=com"
The password for each user is the same as that user's "uid".
There is an Access Control List that prevent display of the userPassword hash to anybody except the Manager `"cn=Manager,dc=renbud,dc=com" -w secret`
After that, `make slapd` will launch the `slapd` daemon. It does so with a set of configuration files that allow it to respond to the `radiusclient` demonstration.
The RADIUSOV demonstration also authenticates against the FreeRADIUS `radiusd` daemon; you can launch that here with `make radiusd`.
In general, you'll want to launch them both with `launch both`.
You can shut down `slapd` and `radiusd` with `make kill`.
Note: Although I took pains to make sure that this local instance of slapd and its configuration files do not interfere with any other installation of OpenLDAP, I didn't make an effort to change the name. Thus, my ham-handed `make kill` will kill not just this local instance of `slapd` but any others that might be running.
This diff is collapsed.
all:
@echo "Hi!"
cp mod-ldap.original mod-ldap
sed -i '/port = 389/a \\tport = 3899' mod-ldap
sed -i "/identity = /a\ \tidentity = \'cn=Manager,dc=renbud,dc=com\'" mod-ldap
sed -i "/password = /a\ \tpassword = secret" mod-ldap
sed -i 's/dc=example,dc=org/dc=renbud,dc=com/g' mod-ldap
scp mod-ldap /usr/local/etc/raddb/mods-available/ldap
ln -f -s /usr/local/etc/raddb/mods-available/ldap /usr/local/etc/raddb/mods-enabled/ldap
# Disable ldap by uncommenting the following line
rm /usr/local/etc/raddb/mods-enabled/ldap
cp clients.conf.original clients.conf.mod
sed -i -e '$$aclient lap90a90 {' clients.conf.mod
sed -i -e '$$a\\tipaddr = 10.0.1.250' clients.conf.mod
sed -i -e '$$a\\tsecret = testing123' clients.conf.mod
sed -i -e '$$a\\trequire_message_authenticator = yes' clients.conf.mod
sed -i -e '$$a}' clients.conf.mod
cp clients.conf.mod /usr/local/etc/raddb/clients.conf
Modules in Version 3
====================
As of Version 3, all of the modules have been placed in the
"mods-available/" directory. This practice follows that used by other
servers such as Nginx, Apache, etc. The "modules" directory should
not be used.
Modules are enabled by creating a file in the mods-enabled/ directory.
You can also create a soft-link from one directory to another::
$ cd raddb/mods-enabled
$ ln -s ../mods-available/foo
This will enable module "foo". Be sure that you have configured the
module correctly before enabling it, otherwise the server will not
start. You can verify the server configuration by running
"radiusd -XC".
A large number of modules are enabled by default. This allows the
server to work with the largest number of authentication protocols.
Please be careful when disabling modules. You will likely need to
edit the "sites-enabled/" files to remove references to any disabled
modules.
Conditional Modules
-------------------
Version 3 allows modules to be conditionally loaded. This is useful
when you want to have a virtual server which references a module, but
does not require it. Instead of editing the virtual server file, you
can just conditionally enable the module.
Modules are conditionally enabled by adding a "-" before their name in
a virtual server. For example, you can do::
server {
authorize {
...
ldap
-sql
...
}
}
This says "require the LDAP module, but use the SQL module only if it
is configured."
This feature is not very useful for production configurations. It is,
however, very useful for the default examples that ship with the
server.
Ignoring module
---------------
If you see this message::
Ignoring module (see raddb/mods-available/README.rst)
Then you are in the right place. Most of the time this message can be
ignored. The message can be fixed by finding the references to "-module"
in the virtual server, and deleting them.
Another way to fix it is to configure the module, as described above.
Simplification
--------------
Allowing conditional modules simplifies the default virtual servers
that are shipped with FreeRADIUS. This means that if you want to
enable LDAP (for example), you no longer need to edit the files in
raddb/sites-available/ in order to enable it.
Instead, you should edit the raddb/mods-available/ldap file to point
to your local LDAP server. Then, enable the module via the soft-link
method described above.
Once the module is enabled, it will automatically be used in the
default configuration.
Multiple Instances
------------------
It is sometimes necessary to have the same module do two different
things. The server supports this functionality via "instances" of
modules.
Normally, a module configuration looks like this:
sql {
... sql stuff ...
}
This module is then refereed to as the "sql" module.
But what happens if you want to connect to two different SQL
databases? The solution is simple; copy the "sql" module
configuration, and add an instance name after the "sql" string:
sql mysql1 {
... configuration for connecting to mysql11 ...
}
sql mysql2 {
... configuration for connecting to mysql12 ...
}
This configuration says "load the SQL module, but create two copies of
it, with different configurations". The different configurations can
be referred to by name, as "mysql1" and "mysql2". That is, anywhere
you would normally use "sql", you could use either "mysql1" or
"mysql2".
For further examples of using module instances, see the "attr_filter"
module configuration in this directory.
# -*- text -*-
##
## clients.conf -- client configuration directives
##
## $Id: 60f9f4bf8a32804182e4516ac69ac510d25215d1 $
#######################################################################
#
# Define RADIUS clients (usually a NAS, Access Point, etc.).
#
# Defines a RADIUS client.
#
# '127.0.0.1' is another name for 'localhost'. It is enabled by default,
# to allow testing of the server after an initial installation. If you
# are not going to be permitting RADIUS queries from localhost, we suggest
# that you delete, or comment out, this entry.
#
#
#
# Each client has a "short name" that is used to distinguish it from
# other clients.
#
# In version 1.x, the string after the word "client" was the IP
# address of the client. In 2.0, the IP address is configured via
# the "ipaddr" or "ipv6addr" fields. For compatibility, the 1.x
# format is still accepted.
#
client localhost {
# Only *one* of ipaddr, ipv4addr, ipv6addr may be specified for
# a client.
#
# ipaddr will accept IPv4 or IPv6 addresses with optional CIDR
# notation '/<mask>' to specify ranges.
#
# ipaddr will accept domain names e.g. example.org resolving
# them via DNS.
#