Uellue's Blog

HOWTO: Configure WLAN client for EAP-TLS with Linux

This howto describes specificly the WLAN configuration in the Technical Faculty of the Christian Albrechts University in Kiel (TF), but is hopefully also useful for other users who want to configure an EAP-TLS WLAN client with xsupplicant under Linux.

Saber Zrelli has written an article about configuring EAP-TLS with wpa_supplicant.

A description of EAP-TLS WLAN access for the Technical Faculty with wpa_supplicant instead of xsupplicant was written by skaven.

My used hardware and distribution

Needed software

Recommended for testing and debugging:

Get your wireless card to work

This strongly depends on what kind of device you have. To get started have a look at http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/. You can check with iwconfig if a wireless network interface is present:

user@host:~$/sbin/iwconfig
lo        no wireless extensions.

eth0      no wireless extensions.

eth2      IEEE 802.11g  ESSID:"TFconnect"
          Mode:Managed  Frequency:2.412 GHz  Access Point: 00:A0:C5:5C:CC:4D
          Bit Rate:54 Mb/s   Tx-Power:15 dBm
          Retry limit:15   RTS thr:off   Fragment thr:off
          Power Management:off
          Link Quality=82/100  Signal level=-52 dBm  Noise level=-53 dBm
          Rx invalid nwid:0  Rx invalid crypt:2  Rx invalid frag:0
          Tx excessive retries:0  Invalid misc:459   Missed beacon:0

sit0      no wireless extensions.

Here eth2 is recognized as wireless interface.

Now you can start Kismet and see if you are receiving stuff. In the TF a network with ESSID "TFconnect" must appear.

Get and convert the cryptographic keys

In the TF you first need to get a personal account; ask the network administrators.

They will generate an user key and an user certificate and include both in an pkcs12 encoded file. Furthermore you'll get the self-signed certificates for their Certificate Authority (CA) and two subsequent certificates as DER-encoded files. These certificates are used for signing their keys.

Xsupplicant needs the keys in PEM format, so you'll have to use OpenSSL to convert them.

Convert the DER certificates Client.der, Server.der and Root.der to PEM:
  
openssl x509 -inform DER -in Client.der -out Client.pem
openssl x509 -inform DER -in Root.der -out Root.pem
openssl x509 -inform DER -in Server.der -out Server.pem
  

Then you should move the .der files somewhere else so that they don't lead to confusion.

Convert the pkcs12 file xyz.p12 to PEM format (in the TF xyz is your account name):
  
openssl pkcs12 -CApath /path/to/pem/certificates -in xyz.p12 -clcerts -out xyz.pem
  

OpenSSL will ask you for the import passwort of the pkcs12 file (empty in the TF) and for a passphrase for the private key in the PEM file. The PEM file now contains your user certificate and your private key. Keep this private key secret, i.e. make sure nobody you don't trust can read it.

Configure Xsupplicant

In Debian the configuration file is at /etc/xsupplicant/xsupplicant.conf. After installation there's a sample configuration that explains the configuration options. Move this file somewhere else so that it's not overwritten, e.g. /ect/xsupplicant/xsupplicant.conf.old

Configuration for the TF based on the sample configuration:
  
# network_list: defines all of the networks in this file which
#      should be kept in memory and used.Comma delimited list or "all"
#      for keeping all defined configurations in memory. For efficiency,
#      keep only the networks you might roam to in memory.
#      To avoid errors, make sure your default network is always
#      in the network_list.  In general, you will want to leave this set to
#      "all".

network_list = all
#network_list = default, test1, test2

# default_netname: some users may actually have a network named "default".
#      since "default" is a keyword in the network section below, you can
#      change which is to be used as the replacement for this keyword
#
# As of Xsupplicant 1.2.2, wireless interfaces will no longer use the default
# network name if they are unable to find a valid config.  If you have
# auto association turned on, Xsupplicant will find a new network to connect
# to.  Otherwise, it will do nothing.

default_netname = default

# When running in daemon, or non-foreground mode, you may want to have the
# output of the program.  So, define a log file here.  Each time XSupplicant
# is started, this file will be replaced.  So, there is no need to roll the
# log file. If the logfile name is set to "syslog", then all messages will
# be sent to the syslog. If syslog is defined, you should also define
# "log_facility" to specify which logging facility will be used.
logfile = syslog

# If you have set the logfile option to "syslog", then you should define
# log_facility in order to tell Xsupplicant where to send log messages.
# Valid settings are cron, daemon, ftp, kern, local0, local1, local2,
# local3, local4, local5, local6, local7, lpr, news, user, and uucp
log_facility = daemon

# The "default_interface" is the interface that will be used if one is not
# specified on the command line.

#default_interface = eth1

# We know the interface from the iwconfig output
default_interface = eth2

## Default Network Section
# This is the network configuration that will be used in the event that
# no valid network configuration can be found.  If you are going to leave
# Xsupplicant running all the time, it is recommended that you leave this
# section blank.  A blank network definition will result in Xsupplicant
# turning off encryption and turning control over to iwconfig.
default
{
}

# The network's ESSID, in case of the TF it's TFconnect

TFconnect
{
  # allow_types:  describes which EAP types this network will allow.  The
  # first type listed will be requested if the server tries to use something
  # not in this list.
  # allow_types = eap_tls, eap_md5, eap_gtc, eap-otp
  allow_types = all

  # identity:  what to respond with when presented with an EAP Id Request
  #   Typically, this is the username for this network. If this is a string
  # that does not contain any spaces, or unusual characters, it can be listed
  # plain.  Otherwise, it should be enclosed in quotes.

  # your account name
  identity = xyz

  ## method-specific parameters are kept in the method
  eap_tls {
     # Certificate and key are contained in the same file here
     user_cert = /path/to/user/cert/xyz.pem
     user_key  = /path/to/user/cert/xyz.pem
     # What you entered as passphrase when you converted the pkcs12 file
     user_key_pass = "secret"
     root_cert = /path/to/certificates/Root.pem
     # Directory containing Client.pem, Server.pem and Root.pem
     root_dir = /path/to/certificates/
     #crl_dir = /path/to/dir/with/crl
     chunk_size = 1398
     # /dev/random is more secure, but too slow
     random_file = /dev/urandom

     # To enable TLS session resumption, you need to set the following
     # value to "yes".  By default, session resumption is disabled.
     #session_resume = yes
  }
}
  

See the Xsupplicant documentation and the sample configuration file for other options.

Test the configuration

First we have to join the TFconnect network:

  
host:~# iwconfig eth2 essid "TFconnect"
  

Now start Xsupplicant as root on the command line on interface eth2 (-i eth2) using our configuration file (-c /etc/xsupplicant/xsupplicant.conf) in the foreground (-f) with verbose debugging (-d A) to test the configuration file. This will produce a lot of text, you might want to redirect it to a file to analyze it in case something goes wrong.

The connection is only established completely at the moment the interface is "up". It takes some time (like, 30 s) until the connection is finally established. We open another shell to activate the network interface eth2 and to configure it.

  
host:~# xsupplicant -c /etc/xsupplicant/xsupplicant.conf -i eth2 -f -d A
[STATE] Reinit state machine
[STATE] [backend_sm] REQUEST -> INITIALIZE
[STATE] [backend_sm] INITIALIZE -> IDLE
[STATE] [backend_sm] UNKNOWN -> INITIALIZE
[STATE] [backend_sm] INITIALIZE -> IDLE
[INT] Initializing socket for interface eth2..
[INT] Allmulti mode is already enabled on this device!
[INT] Interface eth2 is wireless!
Card reported capabilitites : WEP40 WEP104 WPA WPA2 TKIP CCMP
[INT] Interface initialized!
[CONFIG] Working from config file /etc/xsupplicant/xsupplicant.conf.
No configuration information for network "(null)" found.  Using default.
[INT] Opened socket descriptor #5
[INT] Interface eth2 is wireless!
Your card is currently set for wireless network "TFconnect".  Looking for a config.
[CONFIG] Working from config file /etc/xsupplicant/xsupplicant.conf.
[STATE] Init wireless state machine.
UNASSOCIATED -> ACTIVE_SCAN
[STATE] Reinit state machine
[STATE] [backend_sm] IDLE -> INITIALIZE
[STATE] [backend_sm] INITIALIZE -> IDLE
Scanning for wireless networks ...
[...]
[ALL] Clock tick! authWhile=0 heldWhile=0 startWhen=0 curState=AUTHENTICATED
[ALL] Clock tick! authWhile=0 heldWhile=0 startWhen=0 curState=AUTHENTICATED
[ALL] Clock tick! authWhile=0 heldWhile=0 startWhen=0 curState=AUTHENTICATED
[ALL] Clock tick! authWhile=0 heldWhile=0 startWhen=0 curState=AUTHENTICATED
[...]
  
If there's no error you'll finally get this output from xsupplicant. On another shell we activate the interface and try to get a network configuration via DHCP:
  
host:~# ifconfig eth2 up
host:~# dhclient eth2
Internet Systems Consortium DHCP Client V3.0.4
Copyright 2004-2006 Internet Systems Consortium.
All rights reserved.
For info, please visit http://www.isc.org/sw/dhcp/

Listening on LPF/eth2/00:11:22:33:aa:bb
Sending on   LPF/eth2/00:11:22:33:aa:bb
Sending on   Socket/fallback
DHCPREQUEST on eth2 to 255.255.255.255 port 67
DHCPACK from 192.168.0.1
bound to 192.168.0.150 -- renewal in 18844 seconds.
  

If everything goes right we get an IP address. If there's no DHCP server running in your network you have to set IP address, netmask, routing information, DNS servers etc. by hand.

Automating stuff

This section is still incomplete, I so far only figured out how to control Xsupplicant with the init script /etc/init.d/xsupplicant. On Debian you have to edit /etc/default/xsupplicant and to set ENABLED=1 and to give suitable arguments, like ARGS="-i eth2 -c /etc/xsupplicant/xsupplicant.conf". I am still working on a way to configure the interface via /etc/network/interfaces or even to join the right network automatically.