8.4 FreeRadius

The FreeRadius server project (http://www.freeradius.org/) is the implementation of the Remote Authentication Dial-In User Service (RADIUS) protocol used by many corporations and Internet service providers to authenticate users connecting from remote locations. Complete coverage of FreeRadius or RADIUS servers goes beyond the scope this chapter. RFC 2865 explains the details of the protocol. For a more practical look at RADIUS, you should refer to the FreeRadius web site as well as RADIUS, by Jonathon Hassel (O'Reilly).

The FreeRadius server daemon, radiusd, can use an LDAP directory in two different ways. First, it can use LDAP as a data store for RADIUS attribute values. RADIUS attributes are defined by the RADIUS protocol and should not be confused with LDAP attributes.[4] The only similarity between the two types of attributes is that both have names and are used to store values. The FreeRadius administrator defines the mapping between RADIUS attributes and the LDAP attributes used to represent them. We'll look at the configuration details after we have compiled a working RADIUS server. The second option is to use the directory as an authentication service by binding to the LDAP server on behalf of a user. In this way, radiusd can determine whether to accept or reject incoming connection requests.

[4] A list of RADIUS attributes linked with the corresponding RFCs can be found at http://www.freeradius.org/rfc/attributes.html.

In the 0.8 release, the rlm_ldap module used by radiusd to access a directory is included in a default installation. No additional flags are required to enable LDAP support at compile time. Running the basic configure && make && /bin/su -c "make install" is enough to achieve a working radiusd in most environments.

Without getting too bogged down in the specifics of the FreeRadius configuration file, radiusd.conf, it is worth explaining the general layout. Configuration options can be described as either existing within the scope of a section bounded by { }s or global. Global parameters define information such as the location of directories necessary to the general operation of radiusd or the number of threads that the main server should spawn. Scoped parameters can be subdivided into module settings and component implementations.

FreeRadius modules are shared libraries defined by the project's RLM interface. The modules block in radiusd.conf contains parameters specific to each library. The RLM interface describes several different components that a module can implement. The two components of interest to us are authorize and authenticate.

The authorization component is used by radiusd to look up information about a user account. The authorize section can contain several different module names. Each module is queried in order for an entry matching the login name of the user in question until a record is located or all modules have reported failure. Part of the authorization component's responsibility is to describe the authentication method used to validate this account. The authenticate section defines possible authentication mechanisms. The method actually used for a specific request is determined by the information returned by the authorize section.

Here is the working configuration file for a basic server to authenticate connections against the list of local accounts:

## radiusd.conf: FreeRADIUS server configuration file
## Global parameters: directory/logfile locations, etc.
prefix = /opt/radius
exec_prefix = ${prefix}
sysconfdir = ${prefix}/etc
localstatedir = ${prefix}/var
sbindir = ${exec_prefix}/sbin
logdir = ${localstatedir}/log/radius
raddbdir = ${sysconfdir}/raddb
radacctdir = ${logdir}/radacct
confdir = ${raddbdir}
run_dir = ${localstatedir}/run/radiusd
log_file = ${logdir}/radius.log
libdir = ${exec_prefix}/lib
pidfile = ${run_dir}/radiusd.pid
$INCLUDE  ${confdir}/clients.conf
modules {
    ## Unix /etc/passwd-style authentication
    unix {
        passwd = /etc/passwd
        shadow = /etc/shadow
        group = /etc/group
        radwtmp = ${logdir}/radwtmp
    ## Local files. The user's file contains a single entry to default all
    ## authentication to the local system. 
    ##     DEFAULT Auth-Type := System
    files {
        usersfile = ${confdir}/users
        acctusersfile = ${confdir}/acct_users
        compat = no
## Authorization: obtain information about the user 
authorize {
## Authentication: validate the user request
authenticate {

To test your server, you must make sure that the following entry is defined in radiusd's clients.conf file to allow connections over the loopback interface:

## Allow connection requests from localhost.
client {
    secret = testing123
    shortname = localhost
    nastype = other

You can test your configuration by starting radiusd in debug mode. This will produce a large amount of log information printed to standard output.

root# radiusd -X -A
< . . . preceding output omitted . . . >
Ready to process requests.

Using radtest(1), you can verify that the local user guest1 with password test1 can be successfully authenticated:

$ radtest guest1 test1 localhost 0 testing123
Sending Access-Request of id 50 to
   User-Name = "guest1"
   User-Password = "\263\033\037\2760@3\022X\327\334\343\025\265\347}"
   NAS-IP-Address = garion
   NAS-Port = 0
rad_recv: Access-Accept packet from host, id=50, length=20

Now that you have a working RADIUS configuration, it is time to move on and integrate the new server with your directory.

8.4.1 FreeRadius and OpenLDAP

If you want the RADIUS server to utilize the directory for authentication only, no schema modifications to your existing LDAP server are necessary. You can simply use the posixAccount entry for a user, as you did with the ProFTPD server.

The first step is to define the parameters for the rlm_ldap module instance. All of the parameters shown here should be intuitive. The module will perform an anonymous bind to our LDAP server and search for a posixAccount entry whose uid attribute matches the username of the connecting user. Once this entry is found, the library will attempt to bind to the directory as the user to verify the user's credentials. All of the communication takes place after the StartTLS command has been executed to ensure privacy.

ldap {
    server = "ldap.plainjoe.org"
    port = "389"
    basedn = "ou=people,dc=plainjoe,dc=org"
    filter = "(&(objectclass=posixAccount)(uid=%{Stripped-User-Name:-%{User-Name}}))"
    start_tls = yes

There are many more parameters that can be defined for the rlm_ldap module. A complete list is given, along with descriptions, in Table 8-4.

Table 8-4. rlm_ldap module parameters






The attribute located below the basedn that must exist in the user's entry. The user is denied access if the attribute is not returned by the initial search.



Controls how the access_attr directive is used. When disabled, the presence of the access_attr in an entry will deny the user access.



Searches base DN.



Specifies whether the module should compare the check items in the RADIUS request with the check items in the directory.



DN of the entry containing the default RADIUS profile.



Location of the file containing the RADIUS/ LDAP attribute mappings.



An RFC 2254 search filter.



Attribute used when searching for a RADIUS groupname.



The attribute containing the DN of the group of which the user is a member.



The RFC 2254 search filter used to query a group for membership.




DN and password to use when performing a nonanonymous bind to the directory server for searches.



Number of seconds until the LDAP client library cache expires. A setting of 0 disables the cache.



The cache size to pass to the LDAP client libraries. A size of 0 specifies an unlimited size.



The total number of LDAP connections to maintain for the RADIUS server.



OpenLDAP debug flags (see slapd.conf's loglevel parameter).



The number of seconds to wait for a response from the LDAP server in the event of a network failure.



Header (such as {CRYPT}) to strip from the beginning of a password before performing a compare operation against checkItems in the request.



The name of the attribute containing the password for a user.



TCP port to use when contacting the directory server.



The attribute containing the DN of the user's radiusprofile object.



Hostname of the LDAP server.



If enabled, the module will send a StartTLS command prior to any other LDAPoperations.



Number of seconds the LDAP server has to perform the search (server-side time limit).



Number of seconds to wait for a response to the LDAP query.



If enabled, the module will contact the directory using LDAPS.

The next step required to put this new module instance into play is adding a definition to the authenticate section that can be used as a value for the Auth-Type attribute in the users file:

authenticate {
    authtype LDAP {

Now you can change the authentication default in raddb/users to LDAP:

## raddb/users file defined by the files authorize component
## Authenticate all users by binding to the LDAP directory.

After restarting radiusd (again in debug mode), test the new configuration using a preexisting LDAP user entry (uid=kristi and userPassword=testpass):

$ ./radtest kristi testpass localhost 0 testing123
Sending Access-Request of id 147 to
    User-Name = "kristi"
    User-Password = "1q\325\026\020\315p\214X\310\227\376\014]F\332"
    NAS-IP-Address = garion
    NAS-Port = 0
rad_recv: Access-Accept packet from host, id=147, length=20

From the client's point of view, nothing appears to be different. The server, however, yields much more information. You should be able to locate a line where the module binds to the directory on behalf of the user.

auth: type "LDAP"
modcall: entering group authtype
< . . . reminaing output omitted . . . >
rlm_ldap: bind as uid=kristi,ou=people,dc=plainjoe,dc=org/test to 
rlm_ldap: waiting for bind result ...
rlm_ldap: user kristi authenticated successfully
  modcall[authenticate]: module "ldap" returns ok

This is definitely the least intrusive way to integrate FreeRadius with an existing directory and will work with any LDAPv3 server. The next step is to use your directory as a data store for the information currently stored in FreeRadius's users file. This, however, will require that you learn about some new schema items.

The FreeRadius project provides an LDAP schema file for use with OpenLDAP 2.x servers. The RADIUS-LDAPv3.schema file can be found in the doc/ directory of the FreeRadius source code distribution. The schema defines many new attributes used to store RADIUS attribute values and a single structural object class named radiusprofile, shown in Figure 8-5, which is used to represent RADIUS users. To keep our focus on LDAP and not RADIUS, we will only concern ourselves with how radiusAuthType maps the Auth-Type RADIUS attribute. Descriptions of the other attributes can be found in the RADIUS RFC.

Figure 8-5. The radiusprofile object class used by the FreeRadius server

First, copy the new schema file to /usr/local/etc/openldap/schema/:

root# cp RADIUS-LDAPv3.schema /usr/local/etc/openldap/schema/

Next, you must include a reference to this file in slapd.conf. Because a radiusprofile object includes the lmPassword and ntPassword attributes from the samba.schema file, it must be placed after the latter file has been parsed:

## /usr/local/etc/openldap/slapd.conf
## core.schema is required for all servers.
include  /usr/local/etc/openldap/schema/core.schema
## Included from Chapter 6
include  /usr/local/etc/openldap/schema/cosine.schema
include  /usr/local/etc/openldap/schema/nis.schema
## Included from Chapter 7
include  /usr/local/etc/openldap/schema/inetorgperson.schema
## Dependencies for samba.schema: cosine.schema and inetorgperson.schema needed to
## support --with-ldapsam in Samba 
include  /usr/local/etc/openldap/schema/samba.schema
## Support for FreeRadius depends on samba.schema for LM/NT password attributes.
include /usr/local/etc/openldap/schema/RADIUS-LDAPv3.schema

The mapping between LDAP attributes and RADIUS attributes is stored in a text file named ldap.attrmap by default. Conventionally, this file is stored in the raddb/ directory, but both the name and location are configurable via the rlm_ldap module's dictionary_mapping parameter. Because we will be using the default schema file, the corresponding attribute dictionary is sufficient. If you decide to use a custom schema, you may have to modify the dictionary as well.

Your next hurdle is decide where to store the radiusprofile entries in the directory. Your first choice might be to store them with the other user account information below the people ou. The problem is that you cannot add the radiusprofile object to your existing users' accounts. You can either change the radiusprofile definition to an auxiliary object or choose to store objects of this type somewhere else. Following the trend of using provided schemas whenever possible, we will create a new organizational unit to hold FreeRadius users and establish a link between the posixAccount and the radiusprofile objects.

First, create a new ou=radius entry below the services ou (created in Chapter 7 for Sendmail):

dn: ou=radius,ou=services,dc=plainjoe,dc=org
objectclass: organizationalUnit
ou: radius

Next, create a profile for the user kristi. The difficulty with this is choosing the RDN for the entry. You have already chosen the uid attribute as the unique naming convention for entries below ou=people. However, the radiusprofile object includes only the cn attribute. The informational RFC 2377 defines an auxiliary uidObject class for situations such as this one. This allows you to include the uid attribute as the RDN for new entries regardless of the structural object class and still maintain your internal naming conventions. You could just use an extensibleObject, but the uidObject is a cleaner approach. Here is the resulting RADIUS user entry:

dn: uid=kristi,ou=radius,ou=services,dc=plainjoe,dc=org
objectclass: radiusprofile
objectclass: uidObject
uid: kristi
cn: Kristi Carter
radiusAuthType: LDAP

Finally, link the posixAccount entry for kristi to her RADIUS information by storing the DN of the radiusprofile object in uid=kristi,ou=people,dc=plainjoe,dc=org. This time we will use the extensibleObject to add the extra attribute. To make the new attributes easier to see, most of the existing optional attributes have been omitted from this LDIF excerpt. Added attributes are shown in bold.

## Existing optional attributes have been omitted from the display.
dn: uid=kristi,ou=people,dc=plainjoe,dc=org
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: sambaAccount
objectclass: extensibleObject
cn: Kristi Carter
cn: Kristi W. Carter
sn: Carter
loginShell: /bin/bash
uidNumber: 781
gidNumber: 100
homeDirectory: /home/kristi
uid: kristi
rid: 2570
radiusprofileDN: uid=kristi,ou=radius,ou=services,dc=plainjoe,dc=org

The two module configuration changes to be made are changing the locations of the dictionary file for LDAP/RADIUS attributes (dictionary_mapping) and specifying the LDAP attribute containing the DN of the user's RADIUS information (profile_attribute):

ldap {
    server = "ldap.plainjoe.org"
    port = "389"
    basedn = "ou=people,dc=plainjoe,dc=org"
    filter = "(&(objectclass=posixAccount)(uid=%{Stripped-User-Name:-%{User-Name}}))"
    start_tls = yes
    profile_attribute = "radiusProfileDn"
    dictionary_mapping = ${raddbdir}/ldap.attrmap

You can now remove the local copy of the users file from the RADIUS server and add the ldap module to the authorize block in radiusd.conf. The files module is still listed because it also contains directives affecting local accounting policies.

## Authorization: obtain information about the user 
authorize {

This example brings up two important points:

  • Relationships between entries in a directory can be represented by storing the DNs as reference links.

  • Using auxiliary objects such as the uidObject to maintain a standard naming convention can help reduce the management costs associated with locating related entries.

I'll leave it up to you to use the radtest tool to verify that the server is working correctly.