FreeRADIUS + 802.1x/WPA + OpenLDAP HOWTO

Document last changed: $Date: 2005-05-14 19:02:54 -0600 (Sat, 14 May 2005) $, Revision: $Rev: 17 $



This HOWTO covers how to set up your OpenLDAP directory to support 802.1x and WPA users. It assumes you have a working LDAP authentication setup ie. you have your e.g. Linux/Windows hosts authenticating against your LDAP database. If that is not so please consult LDAP authentication for Linux/UNIX/Mac OS X.

If you are only looking to authenticate WPA users that are in your OpenLDAP directory you may not need to follow steps in the following section. Following section is only for those people who want to e.g. dynamically assign users to a VLAN using RADIUS FilterID attributes or other RADIUS type of assignments . If you have no need for that please jump directly to OpenLDAP security.

OpenLDAP setup for 802.1x

1. Add the RADIUS schema to your LDAP configuration ie. if you look at the FreeRADIUS distribution you will find following file

RADIUS-LDAPv3.schema.gz
Uncompress it with
gzip -d RADIUS-LDAPv3.schema.gz

2. Copy the file to your LDAP schema directory and include it  in your slapd.conf ie.

include         /etc/ldap/schema/RADIUS-LDAPv3.schema

Make sure you include it on all LDAP servers ie. Master and Slave servers. If you don't your replication may not work if you have schemacheck set to on.

3. I recommend creating a separate LDAP subtree for 802.1x devices ie. unattended machines. This is not required if you want to provide access to all users. If so please go to following step. Otherwise create a subtree called cn=dot1x,dc=cs,dc=school,edu using a following LDIF

dn: cn=dot1x,dc=cs,dc=,dc=edu
objectClass: container
cn: dot1x
structuralObjectClass: container

To add this LDIF execute

ldapadd -ZZ -x -h ldap1.school.edu -D 'uid=root,cn=users,dc=cs,dc=school,dc=edu' -W

Then cut and paste the above LDIF.

4. Now you are ready to add 802.1x users. Let's say I have a hostname called myhost that I want to authenticate using 802.1x and password to authenticate is supersecret. You would add LDIF like this

dn: uid=myhost,cn=dot1x,dc=cs,dc=school,dc=edu
objectClass: top
objectClass: radiusprofile
objectClass: inetOrgPerson
cn: myhost
sn: myhost
uid: myhost
description: 802.1x user
radiusFilterId: "Enterasys:version=1:policy=Enterprise User"
userPassword: supersecret

Please note that userPassword attribute contains an unencrypted password. radiusFilterID is an additional piece of information that can be supplied to the switch. It depends from a switch to switch so check the docs for your switch. For example in this particular case Enterasys switch will assign a policy of "Enterprise User" to machine that authenticates as myhost.

OpenLDAP security

Since userPasswords are shown in clear text and since in general you don't want even password hashes exposed to casual observation you need to limit access to the userPassword attribute. To do so we'll create a separate user who has access to the userPassword attribute.

1. Create a user e.g. onex with password oursecret in your LDAP repository

2. Add the necessary access control rules to your slapd.conf or slapd.access.conf. For example I have

access to attrs=userPassword
        by dn="uid=onex,cn=users,dc=cs,dc=school,dc=edu" read
        by dn="uid=root,cn=users,dc=cs,dc=school,dc=edu" write
        by dn="uid=admin,cn=users,dc=cs,dc=school,dc=edu" write
        by anonymous auth
        by self write
        by * none

The above couple lines will allow only root, admin and user themself to read and modify the userPassword attribute for a particular entry. onex user we created will have only read privileges to the attribute.

Note: I tried to even further lock down the access so e.g. onex user could only see cn=dot1x,dc=cs,dc=school,dc=edu subtree however I haven't been successful. I tried adding following above the entry above e.g.

access to dn.sub="cn=dot1x,dc=cs,dc=unm,dc=edu" attrs=userPassword
      by dn="uid=onex,cn=users,dc=cs,dc=school,dc=edu" read 

With no success :-(. If you get this working please let me know.

FreeRADIUS setup

Now we need to configure FreeRADIUS to look up users

1. In /etc/freeradius/radiusd.conf find the modules {} section. Insert following

ldap ldap_1x {
                server = "ldap1.cs.school.edu"
                identity = "uid=onex,cn=users,dc=cs,dc=school,dc=edu"
                password = "oursecret"
                basedn = "dc=cs,dc=school,dc=edu"

                base_filter = "(objectclass=radiusprofile)"
                start_tls = yes
                # This is your Certificate Authority (CA) certificate
                tls_cacertfile = /etc/ldap/csca.crt
                tls_require_cert = "demand"
                # default_profile = "cn=radprofile,ou=dialup,o=My Org,c=UA"
                # profile_attribute = "radiusProfileDn"
                access_attr = "radiusFilterId"
                dictionary_mapping = ${raddbdir}/ldap.attrmap
                authtype = ldap

                ldap_connections_number = 5
                timeout = 4
                timelimit = 3
                net_timeout = 1
}

Note: If you don't care about setting up a separate tree for 802.1x users and don't want to add radiusFilterId attribute for all users who need access you can change access_attr to e.g.

access_attr = "uid"

That will allow anyone who has a uid attribute in their LDAP record (all users) to be allowed.

2. Go to

http://tldp.org/HOWTO/html_single/8021X-HOWTO/#confradius

Follow their EAP setup. The main difference is that HOWTO above uses a built-in passwords file instead of LDAP.

3. When you are done we'll just need to add ldap_1x to both authorize and authentication sections ie.

authorize {
        preprocess
        chap
        mschap
        suffix
        ldap_1x
        eap
}

authenticate {
        Auth-Type PAP {
                pap
        }
        Auth-Type MS-CHAP {
                mschap
        }
        eap
}

4. In /etc/freeradius/ldap.attrmap add

checkItem       User-Password                   userPassword

replyItem   Tunnel-Type                            radiusTunnelType
replyItem   Tunnel-Medium-Type             radiusTunnelMediumType
replyItem   Tunnel-Private-Group-Id        radiusTunnelPrivateGroupId

If you are using Samba and you have NT/LM password hashes you should also change the default mapping for LM-Password and NT-Password to following.

checkItem       LM-Password                     sambaLMPassword
checkItem       NT-Password                     sambaNTPassword

5. Start FreeRADIUS in foreground/debug mode and make sure things work :-)

radiusd -X -f


Different LDAP instances depending on source

Solution is from:
http://lists.freeradius.org/archives/freeradius-users/2005/04/frm00783.html
with my own modifications since it didn't work "out-of-the-box"

I'm trying to get ldap instances working on a per client basis.  For example, any authentication requests coming from host example1 should be authenticated using the ldap example1 instance, and example2 should be auth'd using the ldap example2 instance.

1) Define multiple ldap instances in the modules section of radiusd.conf, eg

ldap ldap_client1 {
.....
}
ldap ldap_client2 {
.....
}

2) In the authorize section of radiusd.conf, put e.g.

authorize {
        preprocess
        chap
        mschap
        Autz-Type LDAP1 {
           ldap_client1
        }
        Autz-Type LDAP2 {
           ldap_client2
        }
        eap
        files
}

3) Change the authenticate section e.g.

authenticate {
        Auth-Type PAP {
                pap
        }
        Auth-Type CHAP {
                chap
        }
        Auth-Type MS-CHAP {
                mschap
        }
        Auth-Type LDAP1 {
           ldap_client1
        }
        Auth-Type LDAP2 {
           ldap_client2
        }
        eap
}

3) Then in the users file put ie. default to ldap_client1. Otherwise if client comes from x.x.x.2 authenticate against ldap_client2

DEFAULT Client-IP-Address == "x.x.x.2", Auth-Type := LDAP2, Autz-Type := LDAP2

DEFAULT Auth-Type = "LDAP1"
    Fall-Through = Yes

DEFAULT Autz-Type = "LDAP1"

4) Please note that if you are doing WPA or 802.1x authentication you should NOT set Auth-Type. Let FreeRADIUS set Auth-Type to EAP. For example if you had an AP doing WPA your configuration would look something like this.

DEFAULT Client-IP-Address == "x.x.x.3", Autz-Type := LDAP2

DEFAULT Auth-Type = "LDAP1"
    Fall-Through = Yes

DEFAULT Autz-Type = "LDAP1"


EAP-TTLS vs. PEAP

Most clients these days ie. Windows XP and Mac OS X native 802.1x/WPA clients will prefer EAP-PEAP security method. They are fairly similar with the exception that TTLS gives you extra flexibility to specify what kind of authentication to do inside a TLS tunnel ie. PAP, CHAP or MSCHAPv2. Why is that important you ask ? It is important depending what kind of password information you have stored in your LDAP database.
  1. In most cases you will have the MD5 or CRYPT hashes stored in userPassword attribute. If so you will have to use EAP-TTLS with PAP inner tunnel authentication
  2. If you have NT/LM hashes stored because you e.g. use LDAP as backend to Samba you can use either EAP-TTLS with MSCHAPv2 inner tunnel authentication (this is the default) or EAP-PEAP
  3. If you have passwords in plain text stored in userPassword you can use whatever authentication method you want ie. EAP-TTLS, EAP-PEAP or EAP-MD5.
If you have case 1. here is how to set up your clients for EAP-TTLS with PAP.

 Using PEAP with OpenLDAP

If you do need to use PEAP you will have to add NT/LM hashes to every user record in your LDAP directory. Easiest thing is to follow Samba LDAP set up. For example http://www.ofb.net/~jheiss/samba/ldap.shtml. In short you will have to add the samba schema to your OpenLDAP configuration. Then use smbldap-tools to add necessary samba attributes to a user ie.

smbldap-usermod -a testuser

Once you have that you have to reset users' password with smbldap-passwd since you can't convert CRYPT/MD5 or SHA1 hashes to NT/LM hashes. Also make sure that ldap.attrmap file contains following two lines

checkItem       LM-Password                     sambaLMPassword
checkItem       NT-Password                     sambaNTPassword

Only other gotcha is that you cannot use LDAP bind to authenticate users with PEAP. PEAP will always do MSCHAPv2 authentication in the inner tunnel and RADIUS needs access to NT/LM hashes. Therefore you will need to set up a user that has access to sambaLMPassword and sambaNTPassword attributes. Please check OpenLDAP security at the top of this document ie. you will have to have to modify the permissions entry to read

access to attrs=userPassword,sambaNTPassword,sambaLMPassword
        by dn="uid=onex,cn=users,dc=cs,dc=school,dc=edu" read
....


ChilliSpot setup

Above set up will work nicely with ChilliSpot which is a hotspot captive portal.  The only difference is that you need to edit the hotspotlogin.cgi if you only have MD5/CRYPT password hashes in the LDAP database. You need to set

# Uncomment the following line if you want to use ordinary user-password
# for radius authentication. Must be used together with $uamsecret.
$userpassword=1;
$uamsecret="testing123";

In chilli.conf you will need to add the uamsecret that corresponds to the uamsecret above ie.

uamsecret testing123

In this case RADIUS will get password in clear (decoded with shared key) from the Chillispot and attempt LDAP bind. If you have NT/LM password ie. sambaNTpassword and sambaLMpassword attributes you don't have to do above.

Supplicant setup

Xsupplicant

You can get detail instructions on setting up Xsupplicant at

http://tldp.org/HOWTO/html_single/8021X-HOWTO/#xsupplicant

This is the xsupplicant.conf I used. Change items in red to

network_list = default
default_netname = default
first_auth_command = <BEGIN_COMMAND>dhclient %i<END_COMMAND>
logfile = /var/log/xsupplicant.log
allow_interfaces = eth0

default
{
  type = wired
  allow_types = eap_md5
  identity = <BEGIN_ID>myhost<END_ID>

  eap-md5 {
      username = <BEGIN_UNAME>myhost<END_UNAME>
      password = <BEGIN_PASS>supersecret<END_PASS>
  }

}

WPA Supplicant

You can also use WPA supplicant for both 802.1x wired and Wireless connections. To use wpa_supplicant for wired you have to use version 0.40+ and configure wpa_supplicant as follows

ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=0
ap_scan=0
network={
        key_mgmt=IEEE8021X
        eap=MD5
        identity="XUSERNAME"
        password="XPASSWORD"
        eapol_flags=0
}

In some ways working with wpa_supplicant is easier than with Xsupplicant plus you have a single client for both Wired and Wireless connections.

To start it make sure you specify wired driver ie. -Dwired

/opt/wpa_supplicant  -B -w -c /etc/wpa_supplicant.conf -i eth0 -Dwired

Enable 802.1x on a Cisco switch

To set up Cisco switch for 802.1x I consulted following document

http://www.cisco.com/univercd/cc/td/doc/product/lan/cat2950/1216ea2/scg/swg8021x.htm#60637

In short this is what I did.

1. Enable 802.1x on a Cisco switch. You need to adjust the items in red to your own configuration
Switch# configure terminal
Switch(config)# aaa new-model
Switch(config)# aaa authorization network radius
Switch(config)# aaa authentication login admins local
Switch(config)# aaa authentication dot1x default group radius
Switch(config)# username admin password A-very-long-password
Switch(config)# dot1x system-auth-control
Switch(config)# radius-server host 192.168.1.38 auth-port 1812 acct-port 1813 key testing123
Switch(config)# end

After you are done doing this when you try to log back into the switch you will be prompted for a username and password. The same username and password are the ones you specified under username and password. Make sure that the shared secret (key) for the radius server corresponds to the entry in clients.conf in FreeRADIUS configuration.

2. To configure a particular port for 802.1x use following

Switch(config)# interface fastethernet0/1
Switch(config-if)# dot1x port-control auto
Switch(config-if)# end

3. If you have multiple hosts behind a port ie. there is a switch connected to a port you need to enable following when configuring an interface

Switch(config)# interface fastethernet0/1
Switch(config-if)# dot1x port-control auto
Switch(config-if)# dot1x multiple-hosts

4. If you are happy with the config make sure to copy the running config into the startup config ie.

Switch#copy running-config startup-config

Dynamically assigning VLANs on Cisco switches (NOT YET FULLY TESTED)

Now that you got 802.1x going you can dynamically assign users to a VLAN depending on how they authenticate. Please make sure that you added the proper mappings in the ldap.attrmap. If you did all you have to do is add these attributes to users/machines LDAP record ie. if you want to assign user to VLAN 2 you would add something like this

radiusTunnelMediumType: IEEE-802
radiusTunnelType: VLAN
radiusTunnelPrivateGroupId: 2

or fully

dn: uid=myhost,cn=dot1x,dc=cs,dc=school,dc=edu
objectClass: top
objectClass: radiusprofile
objectClass: inetOrgPerson
cn: myhost
sn: myhost
uid: myhost
description: 802.1x user
radiusFilterId: "Enterasys:version=1:policy=Enterprise User"
userPassword: supersecret
radiusTunnelMediumType: IEEE-802
radiusTunnelType: VLAN
radiusTunnelPrivateGroupId: 2


Troubleshooting

1. My Cisco switch doesn't seem to be authenticating and I have set it up to point to the RADIUS server.

Make sure you are have actually enabled dot1x ie.

Switch#show dot1x
Sysauthcontrol                    = Enabled
Dot1x Protocol Version            = 1
Dot1x Oper Controlled Directions  = Both
Dot1x Admin Controlled Directions = Both

Sysauthcontrol needs to show Enabled. If it doesn't make sure you enable it. Please check this step.

2. I am attempting PEAP authentication but I am getting

rlm_mschap: Told to do MS-CHAPv2 for xxx with NT-Password
   "FAILED: No NT/LM-Password". 

This can be due to following reasons
  1. You do not have NT/LM hashes stored in the LDAP database. Your LDAP directory has to have samba schema included in the configuration and NTPassword and LMPassword attributes populated for the user. Those are different than the userPassword attribute. There are two solutions to this problem
    1. Use TTLS with PAP inner tunnel authentication. To do so you will have to configure your clients.
    2. Populate NTPassword and LMPassword hashes by using e.g. smbldap-tools. You will not be able to convert your existing UNIX hashes to NT/LM.
  2. RADIUS user does not have access to NT/LM hashes. You can check here on how to enable that. Attributes you need access to are NTPassword and LMPassword.

Author: Vladimir Vuksan E-mail me
Contributors: Michael Schwartzkopff (Cisco VLAN assignment)