WPA Enterprise with Tomato and Freeradius

I recently picked up a new router for work and wanted to finally get a proper WPA set up. I know, it's 2009, I'm really on the ball here. For some reason, people didn't like using a lengthy, alpha-numeric passphrase with no basis in English. Go figure. So my goal was to use existing username & password combinations from the email server for WPA Enterprise authentication. Plus it had to work on Windows and OS X clients.

The first thing you need is a radius server. This handles the actual authentication and authorizes connections. FreeRADIUS seems pretty popular, works well, and is included in CentOS. Installation was simple using yum.

To avoid sending authentication information in the clear, the radius server needs SSL certificates. I worked off this excellent tutorial to create my own certificate authority and a signed server certificate. To summarize in brief:

Creating a CA:
# mkdir /etc/raddb/CA
# cd /etc/raddb/CA
# mkdir private
# mkdir newcerts
# touch index.txt
# echo '01' > serial
Edit the openssl configuration file (/etc/pki/tls/openssl.cnf on my machine) and change the "dir" setting to "/etc/raddb/CA"
# openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 3650
# openssl x509 -setalias "mynewCA" -outform DER -in cacert.pem -out cacert.der

The self-signed certificate for the new certificate authority is cacert.pem. The file cacert.der is the same certificate in a different format so it can be imported on Windows and OS X.

Create an xpextensions file that looks like this:
# cat xpextensions
[ xpclient_ext]
extendedKeyUsage = 1.3.6.1.5.5.7.3.2
[ xpserver_ext ]
extendedKeyUsage = 1.3.6.1.5.5.7.3.1

Then create & sign a certificate for the server:
# openssl req -new -nodes -keyout server_key.pem -out server_req.pem -days 730
# openssl ca -policy policy_anything -out server_cert.pem -extensions xpserver_ext -extfile ./xpextensions -infiles ./server_req.pem
# cat server_key.pem server_cert.pem > server_keycert.pem

The server key and certificate are combined in a single file, server_keycert.pem. I did not create any client certificates.

Next, configure freeradius to use these certificates:

  1. (Optionally) Delete everything in /etc/raddb/certs except "dh" and "random". If you accidentally delete those two, the linked tutorial does explain how to recreate them.
  2. Copy the cacert.pem and server_keycert.pem files you created to /etc/raddb/certs. Make sure the freeradius user (radiusd on my machine) owns that directory and everything in it.
  3. In the tls section of /etc/raddb/eap.conf, set up the certificates
  4. private_key_password = whatever_password_you_chose
    private_key_file = ${raddbdir}/certs/server_keycert.pem
    certificate_file = ${raddbdir}/certs/server_keycert.pem
    CA_file = ${raddbdir}/certs/cacert.pem
    dh_file = ${raddbdir}/certs/dh
    random_file = ${raddbdir}/certs/random

  5. In the eap section of /etc/raddb/eap.conf, change the default_eap_type to ttls

At this point, the radius server is configured to use the new certificates. You can test it by running radiusd -X as root. If everything is working, it will list a bunch of configuration and debug information, then say it's ready/listening for connections.

To get the router talking to the radius server, you need to set up a radius client. Make an entry like this in /etc/raddb/clients.conf with your router's IP and a secret passphrase:
client router.ip.address {
secret = your_password
shortname = probably_does_not_matter
}

Now configure tomato. Under Basic => Network => Wireless => Security, set type to WPA Enterprise, encryption to whatever you'd like (I'm using TKIP), Shared Secret to your passphrase in /etc/raddb/clients.conf, and Radius Server to the IP of the machine you just configured freeradius on. The server uses ports 1812 and 1813 UDP, so make sure the firewall on the radius server is not blocking them. Save the router settings and you're two thirds done. Unfortunately the last third is the tricky part.

To this point, we've configured the radius server to authenticate users in an encrypted tunnel using EAP-TTLS or EAP-PEAP. The two protocols are more or less identical in functionality and just differ in dialect. OS X speaks either; Windows only does EAP-PEAP.

Inside this encrypted tunnel, you need to choose which protocol to use for actual authentication. There are lots of options. I found this Compatibility Matrix quite useful. If you need to support Windows clients, the options narrow down a bit. The two MS-CHAP protocols and PEAP (which I'm pretty sure is just MS-CHAP in an SSL tunnel) are the only ones that work without installing additional WPA supplicant software. Unfortuantely, these protocols don't work with unix crypt, so referencing the shadow file directly isn't an option.

If you have a samba installation with an smbpasswd file that parallels your unix password files, there's a handy option in the default installation. In /etc/raddb/radiusd.conf, uncomment this section:
passwd etc_smbpasswd {
filename = /etc/samba/smbpasswd
format = "*User-Name::LM-Password:NT-Password:SMB-Account-CTRL-TEXT::"
authtype = MS-CHAP
hashsize = 100
ignorenislike = no
allowmultiplekeys = no
}

as well as the etc_smbpasswd option in the authorize section of the file. This will use the samba password hashes for any MS-CHAP authentication. I'm using this option and it works well. Note that the samba password file needs to be readable by radiusd (chgrp radiusd smbpasswd & chmod 640 smbpasswd is one option) and MS-CHAP versions are not interchangeable; the client machine must use whatever authentication protocol is specified here.

Without a samba password file, PEAP is the best option. OS X can do this out of the box; just set up a custom configuration for 802.1x in Internet Connect. For Windows, you need some 3rd party software. SecureW2 looks like the most robust and user-friendly option, but it isn't free for enterprise use. The old Linux standby wpa_supplicant can also do the job, but it looks significantly harder to install and configure.