FreeRADIUS and CRLs

Abstract
This page deals with getting CRLs working with FreeRADIUS when using OpenSSL
Most pages found when searching for "FreeRADIUS CRL" appear to be people asking for help, and receiving little other than "read the man page" or "read the config file", neither of which contain any clear information! 
Please read ALL of the page, as it's all relevant, and please send some feedback via the Contact form if you found it useful!

Detail
FreeRADIUS is a very powerful, free RADIUS server.

Combined with OpenSSL, it can be used to provide highly secure 802.11 wireless networks by restricting access to users by means of digital certificates, so that each user has to have a certificate (issued by the network owner) on their device to access the wireless network (WLAN).

If the network owner then wishes to prevent a given user from accessing the WLAN, they can simply use OpenSSL to revoke the user's certificate, which will prevent them from connecting.

However, there are two problems with FreeRADIUS and OpenSSL, which are:
  1. FreeRADIUS only reads a CRL at startup (HUP does not cause a CRL reread) so must be restarted when a certificate is revoked
    (NOTE: Technically this is a problem with the way OpenSSL works, rather than FreeRADIUS, but it still causes an issue)
  2. The HOWTO documentation does not describe how to get the CRL working in the first place!
So, how do you get it working?

First of, we'll assume the following:
  1. You have installed and configured OpenSSL
    1. You have generated a CA
    2. You have generated a server certificate for your RADIUS server
    3. You have generated one or more client certificates
  2. You have installed FreeRADIUS
    1. You have configured it to use EAP-TLS, along with the server certificate and CA public key.
    2. You have configured it to allow your Wireless Access Points (WAPs) access, with a secret key
  3. You have setup your WAPs
    1. They are configured for (most likely) WPA2 Enterprise
    2. They are configured to point to your FreeRADIUS server
    3. They have the secret key setup so they can communicate with FreeRADIUS
  4. You have configured your clients
    1. They have the relevant wireless access point information configured
    2. They have the CA certificate installed
    3. Each user has their relevant client key installed
  5. You have tested the above works, and clients can connect/authenticate
My example assumes you've followed one of the various existing guides to getting WPA2 Enterprise going.  I used the one from Linux Journal's "Paranoid Penguin" series, which is very clear.  You can find that guide here:
If your setup works, and/or you've used the above guide from Linux Journal, then in theory your FreeRADIUS eap.conf file tls section should look something like this (note that I have removed all the majority of the # comment lines):

tls {
certdir = ${confdir}/certs
cadir = ${confdir}/certs
# private_key_password = whatever
private_key_file = ${certdir}/server_key.pem
certificate_file = ${certdir}/server_cert.pem
CA_file = ${certdir}/cacert.pem
dh_file = ${certdir}/dh
random_file = ${certdir}/random
include_length = yes
check_crl = no
CA_path = ${certdir}/
cipher_list = "DEFAULT"
}

The (apparently) obvious line to change to enable CRL checking is check_crl = no, but you'll note there is nowhere in the config to specify where the CRL file lives.  More annoyingly, the eap.conf file contains a comment section which reads:

#  Check the Certificate Revocation List
#
#  1) Copy CA certificates and CRLs to same directory.
#  2) Execute 'c_rehash <CA certs&CRLs Directory>'.
#    'c_rehash' is OpenSSL's command.
#  3) uncomment the line below.
#  5) Restart radiusd


The above implies (to me at least) that you simply put all your files in one place (i.e. wherever ${certdir} is) and run c_rehash . then update eap.conf so that check_crl=yes is set, restart radius and all should work.
  
Except, for most people on the web apparently, it doesn't - and the FreeRADIUS mailing list isn't all that helpful, referring people back to man pages etc, which as mentioned, as thoroughly unclear. 

Also note that probably by default, your "dh" file will be at 512 bytes, not 2048, and the 512 byte ones are more vulnerable to "EXPORT" level hacking, whereby servers can be forced to downgrade connections - so I'd suggest running

openssl dhparam -out dh 2048

...and putting the resultant dh file in the relevant location based on your config.

The problem I found is that my distribution of OpenSSL did not contain c_rehash.  I found a website which contained a script which did the same thing, but that did nothing either.  Basically as soon as check_crl=yes was set, all authentication failed, so obviously something else was required.

So let's assume you are working in your CA certificate directory, and have revoked a certificate by typing:

openssl ca -revoke XXX.pem -keyfile ca.key -cert ca.pem -config ./ca.cnf

...where XXX.pem is/was the certificate to be revoked.
Now you need to generate an actual CRL, which you can do by typing:

openssl ca -gencrl -keyfile ca.key -cert ca.pem -out mycrl.pem -config ./ca.cnf

You should now have a CRL file called mycrl.pem, and you can validate it's contents by typing:

openssl crl -in mycrl.pem -noout -text

You should see a plaintext version of the CRL information, the serial numbers of the revoked keys and the signature algorithm.

The next step is to get FreeRADIUS to find and use this CRL, but if you recall, there were no options to allow you to define where the CRL file lives.  Like me, you may have found a page at http://www.breezy.ca/?q=node/223 which says to add a crl_file value to eap.conf (i.e. crl_file = ${certdir}/mycrl.pem) but that doesn't work either.

The undocumented thing you need to do is concatenate (join) your CA public key and your CRL into one file, like this:

cat ca.pem mycrl.pem > ca_and_crl.pem

This is the key to making it all work!  ca_and_crl.pem now contains both your CA public key and the details of the revoked certificates, in one file.  All that remains to be done now is to update eap.conf changing the CA_file line so that it reads CA_file = ${certdir}/ca_and_crl.pem

Make sure you also set check_crl = yes, then restart FreeRADIUS and try connecting with a bad certificate - it should fail, whereas a valid certificate will work.

The main drawbacks to all this however, are:
  1. You must remember to regenerate your combined CA/CRL file each time you revoke a certificate.
  2. You must fully restart FreeRADIUS so the CA/CRL file is reread (generating a new CA/CRL file is not enough - FreeRADIUS/OpenSSL won't reread it)
  3. If you have many users, or frequently revoke certificates, your CA/CRL file could grow quite large
Failing to keep the CA/CRL file up-to-date will result in any revoked certificate not listed in the CA/CRL file still being able to connect, and if you issue client certificates with long lifespans, then they could potentially connect for as long as their certificate remains valid and absent from the CA/CRL file.

The scripts/commands used above, along with some other useful ones to help manage OpenSSL, are all available from this site.

I hope the above has helped someone! If you (re)use any of the information contained on this (or any other of this sites) page(s), please include a link back here! :)

NOTE: Users of DD-WRT Access Points (APs) may want to read my article on how to disconnect users after their certificate is revoked.