Testing EAP-TLS with freeradius & eapol_test

A project log for Silly software wishlist

Motivation to do some software projects by writing them down.

lion mclionheadlion mclionhead 03/12/2020 at 04:150 Comments

This is only of interest to someone putting together a wireless network of an embedded nature & wanting to prevent unauthorized use by using a protocol called EAP-TLS.  After encountering a need to set up freeradius with eapol_test & finding very little documentation on the subject, the lion kingdom decided to note down the hard earned steps.  Downloading, compiling, & installing freeradius 3.0.20 from source on Ubuntu 16 was actually reasonably straightforward.  

It tried to put all its configuration files in /usr/local/etc/raddb/ but it apparently failed. It didn't create any of the symbolic links.  The lion kingdom deleted /usr/local/etc/raddb/ & copied the raddb inside the build tree to /usr/local/etc/

Comment out filter_username in /usr/local/etc/raddb/sites-available/default
to defeat the realm errors.

Create all the required certificates & keys by following the README:

Some useful nuggets about the file types generated: 

A .crt is a public key with extra information about who owns it.  A .crt is used to verify a peer isn't an imposter.  A .pem is a .crt with even more information.  It is not the same as a .crt.  A .key is a private key.  .keys are never supposed to be seen by anyone but their owners.  

The general idea is the client encodes some piece of data or a hash of some message using its private .key "signing".  The radius server decodes it with the client's public .pem key.  If it decodes properly, the server knows only the true client owning the true private .key "signed" it. 

Of course, to be sure the public .pem key belongs to the client instead of an imposter, we have yet another layer, the certificate authority.  That encodes some certificate authority stuff or a hash of the client's public key with its private ca.key & puts it in the client's .pem file "signing".  The radius server can decode it with the certificate authority's public ca.pem key.  If it decodes properly, the server knows the CA vouched for the identity of the peer by "signing" it with its private .key.

The certificate authority can actually have other certificate authorities "sign" its public keys, creating a "chain of trust".

All these bits combine to form a PKI or "public key infrastructure".

freeradius looks in the ca.cnf, server.cnf, & client.cnf files to find the right settings for making all the certs & keys.  The required changes are given in the README.  For a test environment, it's easier to make all the passwords identical & use simple names like x.  It still needs actual country & state codes.

client.cnf needs a full name@domane.suffix emailAddress to make a unique client .pem for each client or the file will always be just .pem.  It also needs the full domane name to pass other tests.

It took 1 more command to get it to work:
openssl x509 -inform PEM -outform DER -in client.pem -out client.der

A bug in openssl required having a copy of the certificate in .der format or it would complain about being unable to read it.

Freeradius should then run in the foreground & print lots of debugging information with 

radiusd -X

The next step is building the eapol_test client.  This is part of the ages old wpa_supplicant package which is actually inside the hostapd source code & not built by default.  wpa_supplicant has to be built from source to get the eapol_test program.  Go into hostapd*/wpa_supplicant & run make eapol_test

eapol_test needs a config file & a bunch of client certs.  The freeradius README actually created the eapol_test certs & put them in /usr/local/etc/raddb/certs/.  They have to be copied to where eapol_test is going to look for them.

mkdir -p /var/certs/freeradius/

cp ca.pem client.key client.crt /var/certs/freeradius/

Then create an eapol_test.conf file in the wpa_supplicant build directory.  It's just a network section.


The identity & private_key_passwd have to match what was in the  /usr/local/etc/raddb/certs/client.cnf file.  Then, the .pems & .key need to point to the client files you copied.

Finally, run eapol_test in the wpa_supplicant directory with 

eapol_test -c eapol_test.conf -s testing123

The -s parameter needs to have the secret specified in /usr/local/etc/raddb/clients.conf

This should connect to the radius server, do some authentication magic & end by printing SUCCESS or FAILURE


The lion kingdom is a bit hazy on exactly how freeradius decides what clients are authorized & what aren't, in a purely certificate based EAP system that doesn't use login names or passwords.  Theoretically, it relies on the certificate authority & the signing of the certificates. If you create a client CRT with a different CA & try to use it, the server rejects it.  Then of course, you can revoke a certificate.  This is a complicated process, performed in the /usr/local/etc/raddb/certs/ directory.

It begins by using the server's certificate authority to add the client.crt certificate to the index.txt file with a big R indicating revoked.  The index.txt file is some kind of database used by the CA.

openssl ca -revoke client.crt -keyfile ca.key -cert ca.crt -config ./ca.cnf

Then, the index.txt entries have to be written to a certificate revocation list (mycrl.pem) that freeradius can read.

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

Dump the CRL list for testing.

openssl crl -in mycrl.pem -noout -text

The next step is running c_rehash which is a part of the openssl package.

c_rehash .

That creates symbolic links in the certs directory which make all the CRL's visible to freeradius.  The lion kingdom isn't sure this is necessary.  Freeradius may literally check every file in the certs directory for a CRL.

Finally, you have to enable a few options in /usr/local/etc/raddb/mods-enabled/eap

check_crl = yes
check_all_crl = yes

Freeradius must then be restarted to read the CRL.  Helas, if you delete the mycrl.pem file, freeradius still rejects the entries in the CRL.  It may actually only use the entries in the index.txt file & ignore both the mycrl file & the rehashing.