FreeRadius PEAPv0+MSCHAPv2 howto
I've recently been asked to set up a wifi network using user authentication against Active Directory via RADIUS, specifically using the PEAPv0/EAP-MSCHAPv2 protocol combination. This kinda stuff has potential for frustration, but I finally got it to work. Here's how.
First of all, you need an Active Directory domain to authenticate against. MSCHAPv2 seems to actually require this, it looks like you can't use MSCHAPv2 against a passwd file or something. So, get one of those if you haven't already. I set one up using Samba 4.
-
FreeRadius has a sites mechanism much like the Apache2 web server does, and it comes with a few sites preconfigured. Disable the default site:
rm /etc/freeradius/sites-enabled/default
(This only removes a symlink, it does not delete the config altogether.)
-
Create a new file named
/etc/freeradius/sites-available/svedrindefault
:authorize { preprocess mschap suffix eap files } authenticate { Auth-Type MS-CHAP { mschap } eap }
-
Enable the site:
ln -s /etc/freeradius/sites-available/svedrindefault /etc/freeradius/sites-enabled/
-
The svedrindefault tunnel is now used for Phase 1. Phase 2 will use the inner-tunnel configuration, which you should be able to just leave alone and have it work. But just in case, here's my
/etc/freeradius/sites-available/inner-tunnel
:server inner-tunnel { listen { ipaddr = 127.0.0.1 port = 18120 type = auth } authorize { chap mschap suffix update control { Proxy-To-Realm := LOCAL } eap { ok = return } files expiration logintime pap } authenticate { Auth-Type PAP { pap } Auth-Type CHAP { chap } Auth-Type MS-CHAP { mschap } unix eap } session { radutmp } post-auth { Post-Auth-Type REJECT { attr_filter.access_reject } } pre-proxy { } post-proxy { eap } }
-
Edit
/etc/freeradius/modules/mschap
. Here's mine:mschap { use_mppe = yes with_ntdomain_hack = yes ntlm_auth = "/usr/bin/ntlm_auth --request-nt-key --username=%{%{Stripped-User-Name}:-%{%{User-Name}:-None}} --challenge=%{%{mschap:Challenge}:-00} --nt-response=%{%{mschap:NT-Response}:-00}" }
The ntlm_auth line is commented by default and I didn't edit it other than uncommenting it and setting the path. Not sure if it makes any difference, but it works, so I won't complain.
-
Test ntlm_auth:
# ntlm_auth --username=svedrin --password="this is not svedrin's password" NT_STATUS_OK: Success (0x0)
-
Allow freeradius to talk to winbind:
# usermod -a -G winbindd_priv freerad # chown root:winbindd_priv /var/lib/samba/winbindd_privileged/
Don't forget to restart FreeRadius when you're done.
-
Now you should already be able to use the radtest client to authenticate using any domain account:
# radtest -t mschap svedrin@local.lan "this is not svedrin's password" localhost:18120 0 testing123 Sending Access-Request of id 20 to 127.0.0.1 port 18120 User-Name = "svedrin@local.lan" NAS-IP-Address = 127.0.1.1 NAS-Port = 0 Message-Authenticator = 0x00000000000000000000000000000000 MS-CHAP-Challenge = 0xfe62488abc132e2c MS-CHAP-Response = 0x00010000000000000000000000000000000000000000000000000a2220a1a0730c99485ee9963bf41cdac0389546968ee612 rad_recv: Access-Accept packet from host 127.0.0.1 port 18120, id=20, length=84 MS-CHAP-MPPE-Keys = 0x0000000000000000c6c90e3150570e5a59b4c1b7957121b90000000000000000 MS-MPPE-Encryption-Policy = 0x00000001 MS-MPPE-Encryption-Types = 0x00000006
Access-Accept
is the part that tells you it worked. If this works, your Phase 2 is ready to roll. If it doesn't work, well then, sucks to be you. Welcome to the first circle of hell or so.I can definitely recommend running the freeradius daemon in the foreground while debugging. I used tmux to split the console window in half, had
freeradius -Xxx
running in the top pane, and issued my debug commands in the bottom pane. -
Create the certificates your server is going to use to authenticate itself to the clients. (FreeRadius uses Debian's snakeoil certs by default, but those lack a coupl'a features needed for MSCHAPv2.) FreeRadius comes with a set of scripts that should make this pretty easy:
cp -r /usr/share/doc/freeradius/examples/certs /etc/freeradius cd /etc/freeradius/certs vi ca.cnf # some of the defaults might suck, go take a look make
This step should create a bunch'a files in the certs directory, most notably server.key, server.pem and ca.pem. Restart FreeRadius to make sure it knows about them.
-
If you want to test both Phase 1 and Phase 2, you can do so using the eapol_test utility from WPA supplicant. Unfortunately, this thing does not come as part of any Debian package, so you'll have to build it yourself. Here's how:
mkdir wpa cd wpa apt-get build-dep wpa apt-get source wpa cd wpa-2.1 # build the packages to generate .config needed by the eapol_test build dpkg-buildpackage -us -uc cd wpa_supplicant make eapol_test
If this worked, there should be a binary named eapol_test. Now you can create a configuration file for it, e.g.
~/eapol_test.conf
:network={ eap=PEAP eapol_flags=0 key_mgmt=IEEE8021X identity="svedrin@local.lan" password="this is not svedrin's password" ca_cert="/etc/freeradius/certs/ca.pem" phase2="auth=MSCHAPV2" anonymous_identity="anon@local.lan" }
And if you now run eapol_test against this config and your radius server, like so:
./eapol_test -c ~/eapol_test.conf -a127.0.0.1 -p1812 -stesting123 -r1
...it should dump a somewhat huge amount of logging lines and finally conclude with:
EAP: deinitialize previously used EAP method (25, PEAP) at EAP deinit ENGINE: engine deinit MPPE keys OK: 2 mismatch: 0 SUCCESS
SUCCESS is good. We want SUCCESS. If it says FAILURE instead, well then again, sucks to be you.
The good news is that eapol_test is really spammy about what it's doing, and it definitely tells you what's wrong — you just have to be able to identify the meaningful lines, which involves a measurable amount of educated guesses.
-
Add an account for your WiFi access point to
/etc/freeradius/clients.conf
:client localhost { ipaddr = 127.0.0.1 secret = testing123 require_message_authenticator = no } client 192.168.1.191 { # WiFi AP's IP address secret = admin shortname = private-network-1 }
Without this account, FreeRadius won't allow your access point to talk to it. Configure those credentials in your Access Point.
-
Tell your RADIUS server for which domain it is supposed to be authoritative. To do so, amend proxy.conf with a realm stanza such as this one:
realm local.lan { type = radius secret = testing123 authhost = LOCAL accthost = LOCAL }
Now go ahead and try to authenticate a real WiFi client. If you got eapol_test to spit out SUCCESS, this should Just Work™.
Note that even if your client offers a dedicated "Domain Name" field, do not enter the domain name in the "domain name" field. Instead, login using <user>@<domain> in the "user name" field, and leave the domain field empty.
I must say, this hasn't been the most pleasant episode of my life as an IT guy, but I did get it to work eventually. Hopefully I won't have to touch this stuff again anytime soon.