Ubuntu FreeRadius YubiKey

Create and login to a fresh Ubuntu 10.04 LTS machine:

vmbuilder kvm ubuntu \
  --dest /var/lib/libvirt/images/freeradius \
  --proxy http://192.168.1.2/ubuntu \
  --rootsize 10000 \
  --mem 600 \
  --suite lucid \
  --flavour virtual \
  --addpkg unattended-upgrades \
  --addpkg openssh-server \
  --addpkg avahi-daemon \
  --addpkg acpid \
  --ssh-key /root/.ssh/authorized_keys \
  --libvirt qemu:///system \
  --hostname freeradius \
  --bridge br0 \
  --debug
ssh -l root freeradius.local

Install and configure software :

   apt-get install build-essential wget
   apt-get install libpam0g-dev libykclient3 libykclient-dev

Install PAM module:

   wget http://yubico-pam.googlecode.com/files/pam_yubico-2.4.tar.gz
   tar xfz pam_yubico-2.4.tar.gz
   cd pam_yubico-2.4
   ./configure
   make check install
   ln -s /usr/local/lib/security/pam_yubico.so /lib/security/

Setup PAM debug log file:

   touch /var/run/pam-debug.log
   chmod go+w /var/run/pam-debug.log
   tail -F /var/run/pam-debug.log &

Install FreeRadius:

   apt-get install freeradius
   /etc/init.d/freeradius stop

Next we configure FreeRadius. First add this to /etc/freeradius/users:

   DEFAULT Auth-Type = pam

Then comment out pap and uncomment pam from /etc/freeradius/sites-available/default.

Add to the top of /etc/pam.d/radiusd:

   auth sufficient pam_yubico.so id=1 debug authfile=/etc/yubikey_mapping

If you want to use HMAC signing, specify the key= field too, like this:

   auth sufficient pam_yubico.so id=1 key=b64foo debug authfile=/etc/yubikey_mapping

Create a file /etc/yubikey_mapping (ccccccccltnc is Alice’s YubiKey’s public ID) :

   alice:ccccccccltnc

Create a Unix account alice: XXX should not be necessary?

   adduser --disabled-password alice

Just press RET and finally y RET on the prompts.

Start radiusd:

   LD_PRELOAD=/lib/libpam.so.0 freeradius -X

Testing authentication :

Confirm that it works with radtest (use a real OTP from Alice’s YubiKey) :

   radtest alice ccccccccltncdjjifceergtnukivgiujhgehgnkrfcef 127.0.0.1 0 testing123

Output should be like this:

Sending Access-Request of id 69 to 127.0.0.1 port 1812
        User-Name = "alice"
        User-Password = "ccccccccltncdjjifceergtnukivgiujhgehgnkrfcef"
        NAS-IP-Address = 127.0.1.1
        NAS-Port = 0
rad_recv: Access-Accept packet from host 127.0.0.1 port 1812, id=69, length=20

PAM debug output should be like this:

[pam_yubico.c:parse_cfg(404)] called.
[pam_yubico.c:parse_cfg(405)] flags 0 argc 3
[pam_yubico.c:parse_cfg(407)] argv[0]=id=1
[pam_yubico.c:parse_cfg(407)] argv[1]=debug
[pam_yubico.c:parse_cfg(407)] argv[2]=authfile=/etc/yubikey_mapping
[pam_yubico.c:parse_cfg(408)] id=1
[pam_yubico.c:parse_cfg(409)] key=(null)
[pam_yubico.c:parse_cfg(410)] debug=1
[pam_yubico.c:parse_cfg(411)] alwaysok=0
[pam_yubico.c:parse_cfg(412)] verbose_otp=0
[pam_yubico.c:parse_cfg(413)] try_first_pass=0
[pam_yubico.c:parse_cfg(414)] use_first_pass=0
[pam_yubico.c:parse_cfg(415)] authfile=/etc/yubikey_mapping
[pam_yubico.c:parse_cfg(416)] ldapserver=(null)
[pam_yubico.c:parse_cfg(417)] ldap_uri=(null)
[pam_yubico.c:parse_cfg(418)] ldapdn=(null)
[pam_yubico.c:parse_cfg(419)] user_attr=(null)
[pam_yubico.c:parse_cfg(420)] yubi_attr=(null)
[pam_yubico.c:pam_sm_authenticate(452)] get user returned: alice
[pam_yubico.c:pam_sm_authenticate(542)] conv returned: ccccccccltncdjjifceergtnukivgiujhgehgnkrfcef
[pam_yubico.c:pam_sm_authenticate(558)] OTP: ccccccccltncdjjifceergtnukivgiujhgehgnkrfcef ID: ccccccccltnc
[pam_yubico.c:pam_sm_authenticate(583)] ykclient return value (0): Success
[pam_yubico.c:check_user_token(117)] Authorization line: alice:ccccccccltnc
[pam_yubico.c:check_user_token(121)] Matched user: alice
[pam_yubico.c:check_user_token(125)] Authorization token: ccccccccltnc
[pam_yubico.c:check_user_token(128)] Match user/token as alice/ccccccccltnc
[pam_yubico.c:pam_sm_authenticate(625)] done. [Success]

FreeRadius debug output should be like this:

rad_recv: Access-Request packet from host 127.0.0.1 port 38575, id=69, length=89
        User-Name = "alice"
        User-Password = "ccccccccltncdjjifceergtnukivgiujhgehgnkrfcef"
        NAS-IP-Address = 127.0.1.1
        NAS-Port = 0
+- entering group authorize {...}
++[preprocess] returns ok
++[chap] returns noop
++[mschap] returns noop
[suffix] No '@' in User-Name = "alice", looking up realm NULL
[suffix] No such realm "NULL"
++[suffix] returns noop
[eap] No EAP-Message, not doing EAP
++[eap] returns noop
[files] users: Matched entry DEFAULT at line 204
++[files] returns ok
++[expiration] returns noop
++[logintime] returns noop
Found Auth-Type = PAM
+- entering group authenticate {...}
pam_pass: using pamauth string <radiusd> for pam.conf lookup
pam_pass: authentication succeeded for <alice>
++[pam] returns ok
+- entering group post-auth {...}
++[exec] returns noop
Sending Access-Accept of id 69 to 127.0.0.1 port 38575
Finished request 0.
Going to the next request
Waking up in 4.9 seconds.
Cleaning up request 0 ID 69 with timestamp +17
Ready to process requests.

Testing a OTP replay :

Run the command again, with the same OTP :

radtest alice ccccccccltncdjjifceergtnukivgiujhgehgnkrfcef 127.0.0.1 0 testing123

Then output should be like this, since the OTP was replayed:

Sending Access-Request of id 32 to 127.0.0.1 port 1812
        User-Name = "alice"
        User-Password = "ccccccccltncdjjifceergtnukivgiujhgehgnkrfcef"
        NAS-IP-Address = 127.0.1.1
        NAS-Port = 0
rad_recv: Access-Reject packet from host 127.0.0.1 port 1812, id=32, length=20

PAM debug log:

[pam_yubico.c:parse_cfg(404)] called.
[pam_yubico.c:parse_cfg(405)] flags 0 argc 3
[pam_yubico.c:parse_cfg(407)] argv[0]=id=1
[pam_yubico.c:parse_cfg(407)] argv[1]=debug
[pam_yubico.c:parse_cfg(407)] argv[2]=authfile=/etc/yubikey_mapping
[pam_yubico.c:parse_cfg(408)] id=1
[pam_yubico.c:parse_cfg(409)] key=(null)
[pam_yubico.c:parse_cfg(410)] debug=1
[pam_yubico.c:parse_cfg(411)] alwaysok=0
[pam_yubico.c:parse_cfg(412)] verbose_otp=0
[pam_yubico.c:parse_cfg(413)] try_first_pass=0
[pam_yubico.c:parse_cfg(414)] use_first_pass=0
[pam_yubico.c:parse_cfg(415)] authfile=/etc/yubikey_mapping
[pam_yubico.c:parse_cfg(416)] ldapserver=(null)
[pam_yubico.c:parse_cfg(417)] ldap_uri=(null)
[pam_yubico.c:parse_cfg(418)] ldapdn=(null)
[pam_yubico.c:parse_cfg(419)] user_attr=(null)
[pam_yubico.c:parse_cfg(420)] yubi_attr=(null)
[pam_yubico.c:pam_sm_authenticate(452)] get user returned: alice
[pam_yubico.c:pam_sm_authenticate(542)] conv returned: ccccccccltncdjjifceergtnukivgiujhgehgnkrfcef
[pam_yubico.c:pam_sm_authenticate(558)] OTP: ccccccccltncdjjifceergtnukivgiujhgehgnkrfcef ID: ccccccccltnc
[pam_yubico.c:pam_sm_authenticate(583)] ykclient return value (2): Yubikey OTP was replayed (REPLAYED_OTP)
[pam_yubico.c:pam_sm_authenticate(625)] done. [Authentication failure]

FreeRadius debug log:

rad_recv: Access-Request packet from host 127.0.0.1 port 55170, id=32, length=89
        User-Name = "alice"
        User-Password = "ccccccccltncdjjifceergtnukivgiujhgehgnkrfcef"
        NAS-IP-Address = 127.0.1.1
        NAS-Port = 0
+- entering group authorize {...}
++[preprocess] returns ok
++[chap] returns noop
++[mschap] returns noop
[suffix] No '@' in User-Name = "alice", looking up realm NULL
[suffix] No such realm "NULL"
++[suffix] returns noop
[eap] No EAP-Message, not doing EAP
++[eap] returns noop
[files] users: Matched entry DEFAULT at line 204
++[files] returns ok
++[expiration] returns noop
++[logintime] returns noop
Found Auth-Type = PAM
+- entering group authenticate {...}
pam_pass: using pamauth string <radiusd> for pam.conf lookup
pam_pass: function pam_authenticate FAILED for <alice>. Reason: Permission denied
++[pam] returns reject
Failed to authenticate the user.
Using Post-Auth-Type Reject
+- entering group REJECT {...}
[attr_filter.access_reject]     expand: %{User-Name} -> alice
 attr_filter: Matched entry DEFAULT at line 11
++[attr_filter.access_reject] returns updated
Delaying reject of request 1 for 1 seconds
Going to the next request
Waking up in 0.5 seconds.
Sending delayed reject for request 1
Sending Access-Reject of id 32 to 127.0.0.1 port 55170
Waking up in 4.9 seconds.
Cleaning up request 1 ID 32 with timestamp +66
Ready to process requests.