Free 2-factor authentication with RADIUS and HOTP
HOTP is an internet standard that can be used for 2-factor authentication (i.e., something you know and something you have). This is an event-based One-Time Password protocol (i.e., the passwords are generated based on a counter i.o. the current time). This article is a description of how I used free tools to setup a complete environment for two-factor authentication on various servers. The article below is targetted to debian-based systems, but users of other Linux distributions (or other PAM enabled unices) should not have too great a difficulty to adept the description to their own environment.
A free HOTP soft-token for J2ME based mobile phones and PDA's can be found on the Data Security Systems Solutions site (or locally from here, the manual (1.4MB) is here).
On the server-side we use the OTP daemon from Tri-D systems (website no longer available). On that site you could find the source tarball for the HOTP daemon (otpd, now avalailable locally) which is prepared for the debian build tools (and rpm-based systems for that matter). Starting with version 3.0.0, the local state manager daemon (lsmd) is integrated, but with earlier versions this should also be built.
Building the OTP daemon
Unpack the otpd tarball and change to the otpd-3.0.0 directory. We need to make two minor changes, but the files lack the write-bit, so we can just make everything writable with "chmod -R u+w ." The two fixes that we apply are changing a small typo in the debian/changelog and add the install of the otppasswd manpage. Apply the following patch:
-
--- otpd-3.0.0/debian/changelog 2008-01-31 03:30:25.000000000 -0500
-
+++ otpd-3.0.0-1/debian/changelog 2008-02-09 19:45:20.000000000 -0500
-
@@ -2,7 +2,7 @@
-
-
* New release
-
-
- -- Frank Cusack <frank@tri-dsystems.com> Thu Jan 31 2008 00:30:04 -0700
-
+ -- Frank Cusack <frank@tri-dsystems.com> Thu, 31 Jan 2008 00:30:04 -0700
-
-
otpd (2.5.2-1) dapper; urgency=low
-
-
diff -wurN otpd-3.0.0/Makefile.in otpd-3.0.0-1/Makefile.in
-
--- otpd-3.0.0/Makefile.in 2008-01-31 01:32:33.000000000 -0500
-
+++ otpd-3.0.0-1/Makefile.in 2008-02-09 19:49:38.000000000 -0500
-
@@ -73,6 +73,8 @@
-
install-otpd: otpd
-
$(INSTALL) -d $(DESTDIR)$(sbindir)
-
$(INSTALL) otpd $(DESTDIR)$(sbindir)/otpd
-
+ $(INSTALL) -d $(DESTDIR)$(mandir)/man5
-
+ $(INSTALL_DATA) otpd.8 $(DESTDIR)$(mandir)/man5/otppasswd.5
-
$(INSTALL) -d $(DESTDIR)$(mandir)/man8
-
$(INSTALL_DATA) otpd.8 $(DESTDIR)$(mandir)/man8/otpd.8
-
$(INSTALL) -d $(DESTDIR)$(sysconfdir)
After this, the package can be built with
-
dpkg-buildpackage -rfakeroot -uc -us
The pre-build package for the i386 architecture is available here.
If all you want is to use the OTP daemon on a single server, you might install Tri-D's PAM module from their download page. This too is prepared with the debian build files and a spec file for rpm-based systems. I opt for a central server with freeradius and otpd running and having a RADIUS PAM module on the separate systems.
Building the freeradius package
Version 3.0.0 of otpd requires at least version 1.1.7 of freeradius. The rlm_otp module is included, but the debian rules file excludes that module. So to build that we should get the source files from testing. Make sure your /etc/apt/sources.list contains a the line:
deb-src http://ftp.debian.nl/debian/ testing main
then update the apt database and get the source files with
-
apt-get source freeradius
Alternately, you can get the .orig.tar.gz, .diff and .dsc from the debian packages repository and unpack it with
-
dpkg-source -x freeradius_1.1.7-1.dsc
Change to the freeradius-1.1.7 directory and apply the following patch:
-
--- freeradius-1.1.7/debian/rules 2008-01-31 21:05:10.000000000 -0500
-
+++ freeradius-1.1.7-1/debian/rules 2008-02-09 18:25:02.000000000 -0500
-
@@ -65,7 +65,6 @@
-
--without-rlm_eap_tls \
-
--without-rlm_eap_ttls \
-
--without-rlm_eap_peap \
-
- --without-rlm_otp \
-
--with-rlm_sql_postgresql_lib_dir=`pg_config --libdir` \
-
--with-rlm_sql_postgresql_include_dir=`pg_config --includedir` \
-
--without-openssl \
and build the package. Aside from the package itself, various modules are built as separate packages . I only install the basic package (freeradius_1.1.7-1_i386.deb) since flat files are sufficient for my needs.
Combining the two
There was a minor problem I ran into when trying to have freeradius actually use the otpd for verifying the responses: file permissions. otpd has the option of running as a user other than root but then some files and directories have to be readable and/or writable by that user. If you install the package they are not. freeradius runs as user "freerad" by default, and this is the user that will access the socket file in /var/run/otpd. There are two solutions: have freeradius run as root, or have otpd run as freerad and change the file ownerships. I opted for the latter. For this reason I postponed configuring otpd until after I installed freeradius. I also ran into a small bug in the otpd package: the /etc/otpd.conf has an empty "state" section, which is considered a syntax error so the debian post-install script failed. I just unhashed the "mode = local" line and issued "dpkg --configure --pending" to make it happy.
Configuring otpd
First, we stop both the freeradius and otpd daemons. Some files and directories need to be created and the correct permissions set. Execute the following commands:
-
mkdir /etc/otpstate
-
touch /etc/otppasswd
-
chmod 600 /etc/otppasswd
-
chmod 700 /etc/otpstate
-
chown freerad:freerad /etc/otpd.conf /etc/otpstate/ /etc/otppasswd /var/run/otpd/
Generate a token on the J2ME client. The seedlength may be any value from 16 to 20 (I use the maximum), but the OTP length should be 6. This is because of a limitation of the "resynctool" that we will see later. After this the seed is displayed. Make sure you see the complete seed (i.e. opening and closing bracket should both be visible). I use a Windows Mobile based PDA and I need to use landscape mode to prevent the seed being truncated. The seed is displayed only once. Be cautious in copying it in the next step. After this, you are opted to place a PIN code on the token you just created. You might want to do that to have real 2-factor authentication.
Now create an entry in /etc/otppasswd with the following syntax:
user:hotp-d6:key
where "user" is the loginname of the user that will be authenticated via RADIUS, and "key" is the seed you just generated on the soft-token. So the line might look like this:
-
foo:hotp-d6:3cd0f53cd4d94b08d249b1f861e655d774fcf0e5
Now we need a state file for this user. Calculate 2 consecutive passwords with the token, and use "resynctool" to generate state information like this:
-
resynctool -1 990878 -2 457035 -u foo -k 3cd0f53cd4d94b08d249b1f861e655d774fcf0e5
This results in output of the form:
5:foo:0000000000000002:::0:0:0:
Copy this into the file "/etc/otpstate/user" where "user" is the same as the second field from the pasted text (in this case: "foo"). Make sure user freerad has read and write access to the file.
Now start the otpd daemon (/etc/init.d/otpd start) and test it with the "otpauth" command:
-
otpauth -u foo -p 123456 -s /var/run/otpd/socket
If you get the output "5 (service error)", your configuration is still incorrect. Review /var/log/auth.log for clues what should be fixed. The error the we should expect here is "3 (authentication error)". Try the same command with a newly generated password, and you should get "0 (ok)".
Configuring freeradius
We should enable the otp module in freeradius. That means removing the hash to include otp.conf and adding "otp" to both the authorize and authenticate modules of /etc/freeradius/radiusd.conf (or apply the following patch):
-
--- /oldconf/freeradius/radiusd.conf 2008-02-10 00:29:26.000000000 +0100
-
+++ /etc/freeradius/radiusd.conf 2008-02-11 21:59:59.000000000 +0100
-
@@ -1730,7 +1730,7 @@
-
# $INCLUDE ${confdir}/postgresqlippool.conf
-
-
# OTP token support. Not included by default.
-
- # $INCLUDE ${confdir}/otp.conf
-
+ $INCLUDE ${confdir}/otp.conf
-
-
}
-
-
@@ -1788,6 +1788,8 @@
-
# need to setup hints for the remote radius server
-
authorize {
-
#
-
+ otp
-
+ #
-
# The preprocess module takes care of sanitizing some bizarre
-
# attributes in the request, and turning them into attributes
-
# which are more standard.
-
@@ -1906,6 +1908,8 @@
-
#
-
authenticate {
-
#
-
+ otp
-
+ #
-
# PAP authentication, when a back-end database listed
-
# in the 'authorize' section supplies a password. The
-
# password can be clear-text, or encrypted.
Now we can start the RADIUS daemon (/etc/init.d/freeradius start) and test if it works with the otp module:
-
radtest foo 123456 localhost 10 testing123
"testing123" is the default secret for localhost in /etc/freeradius/clients.conf. You should change that. We also don't use a NAS server so the NAS port (10) doesn't matter. We used a wrong password so the output looks like this:
Sending Access-Request of id 177 to 127.0.0.1 port 1812
User-Name = "foo"
User-Password = "123456"
NAS-IP-Address = 255.255.255.255
NAS-Port = 10
Re-sending Access-Request of id 177 to 127.0.0.1 port 1812
User-Name = "foo"
User-Password = "123456"
NAS-IP-Address = 255.255.255.255
NAS-Port = 10
rad_recv: Access-Reject packet from host 127.0.0.1:1812, id=177, length=20
With a correct password we get the following output:
Sending Access-Request of id 172 to 127.0.0.1 port 1812
User-Name = "foo"
User-Password = "752230"
NAS-IP-Address = 255.255.255.255
NAS-Port = 10
rad_recv: Access-Accept packet from host 127.0.0.1:1812, id=172, length=20
Now we need to allow clients to connect to the RADIUS server. For that we edit /etc/freeradius/clients.conf and assign shared secrets to the various clients and/or networks that will be accessing the server. The pre-installed file has comments that should make the syntax clear. Reload the freeradius daemon after you finished editing clients.conf.
Configuring the clients
On each client, install libpam-radius-auth:
-
apt-get install libpam-radius-auth
Then edit /etc/pam_radius_auth.conf and add a line for the RADIUS server with the secret assigned to the specified client and a timeout value.
Now you need to edit those PAM files in /etc/pam.d where you want to allow RADIUS authentication. In debian, there is a single file (/etc/pam.d/common-auth) that is sourced by all modules that allow standard Unix authentication. I edited the file to allow both standard Unix authentication and RADIUS authentication. The file now reads:
-
auth sufficient pam_unix.so nullok_secure
-
auth required pam_radius_auth.so try_first_pass
so I can choose my method of authentication depending on the circumstances.
Caveat emptor
First, the RADIUS daemon requires UDP port 1812 to be open. Make sure your firewall configuration allows that.
Second, since the username used by the authentication module is specified on the RADIUS server, do note that there is a risk of clashing usernames. Either make sure that identical usernames on various clients belong to the same person or that different clients with the same usernames don't share access to the RADIUS server.
Third, since the HOTP protocol is an event-based system, a brute force attack on a simple numerical 6-digit password is very possible! The reason for this is that wrong password probes or a timeslot don't invalidate the current password. Make sure that the service which you use HOTP for is not freely accessible from untrusted networks and/or use something like fail2ban. Also make sure that you do not generate many superfluous passwords with your token that are not communicated to the otpd daemon on the RADIUS server. The server will calculate 6 consecutive passwords and give up if none qualify. If this happens, you will should use "resynctool" to calculate a new content for the state file.
Enjoy!

Leave a Reply