Local Authentication Using Challenge Response

The PAM module can utilize the HMAC-SHA1 Challenge-Response mode found in YubiKeys starting with version 2.2 for offline authentication. This mode is useful if you don’t have a stable network connection to the YubiCloud.

The ykpamcfg utility currently outputs the state information to a file in the current user’s home directory ($HOME/.yubico/challenge-123456 for a YubiKey with serial number API readout enabled, and $HOME/.yubico/challenge for one without).

The PAM module supports a system wide directory for these state files (in case the user’s home directories are encrypted), but in a system wide directory, the challenge part should be replaced with the username. Example: /var/yubico/alice-123456.

To use the system-wide mode, you currently have to move the generated state files manually and configure the PAM module accordingly.

The following process is tested on Ubuntu 12.04.

First install the package:

sudo apt-get install libpam-yubico

You will get a question about the PAM configuration line. Enter this line:

mode=challenge-response

The next question will be about which PAM modules to enable. Don’t enable anything just yet, because you need to program your YubiKey first.

If you have already installed the package or want to reconfigure it, you may use this command:

sudo dpkg-reconfigure libpam-yubico

The next step is to add a challenge-response slot to your YubiKey. If you have a normal YubiKey with OTP functionality on the first slot, you could add Challenge-Response on the second slot. You could have CR on the first slot, if you want.

First, program a YubiKey for challenge response on Slot 2:

ykpersonalize -2 -ochal-resp -ochal-hmac -ohmac-lt64 -oserial-api-visible
...
Commit? (y/n) [n]: y
$

Now, set the current user to require this YubiKey for logon:

mkdir $HOME/.yubico
ykpamcfg -2 -v
...
Stored initial challenge and expected response in '/home/alice/.yubico/challenge-123456'.
$

From security perspective, it is generally a good idea to move the challenge file in a system-wide path that is only read- and writable by root. To do this do as follow:

sudo mkdir /var/yubico
sudo chown root.root /var/yubico
sudo chmod 700 /var/yubico
ykpamcfg -2 -v
...
Stored initial challenge and expected response in '$HOME/.yubico/challenge-123456'.
sudo mv ~/.yubico/challenge-123456 /var/yubico/alice-123456
sudo chown root.root /var/yubico/alice-123456
sudo chmod 600 /var/yubico/alice-123456

It is important that the file is named with the name of the user that is going to be authenticated by this YubiKey.

Finally we tell the pam module where to look for the challenge file

emacs /etc/pam.d/common-auth

and edit the following line as follow:

auth   required        pam_yubico.so mode=challenge-response chalresp_path=/var/yubico

Then back to the PAM configuration step, first make sure you have a root terminal available to be able to disable YubiKey login in case of issues.

sudo -s

Then run the "pam-auth-update" command and enable the Yubico PAM module.

sudo pam-auth-update

You should now be able to authenticate using YubiKey Challenge-Reseponse together with a password like this:

jas@latte:~$ sudo -s
[sudo] password for jas:
root@latte:~#

Now remove the YubiKey and try again (in a new terminal to avoid sudo caching), and you should not be able to login.

For debugging, you can make the PAM configuration line:

mode=challenge-response debug

and then create a log file:

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

and then tail the file. For successful logins it should print something like this:

[pam_yubico.c:parse_cfg(721)] called.
[pam_yubico.c:parse_cfg(722)] flags 32768 argc 2
[pam_yubico.c:parse_cfg(724)] argv[0]=mode=challenge-response
[pam_yubico.c:parse_cfg(724)] argv[1]=debug
[pam_yubico.c:parse_cfg(725)] id=-1
[pam_yubico.c:parse_cfg(726)] key=(null)
[pam_yubico.c:parse_cfg(727)] debug=1
[pam_yubico.c:parse_cfg(728)] alwaysok=0
[pam_yubico.c:parse_cfg(729)] verbose_otp=0
[pam_yubico.c:parse_cfg(730)] try_first_pass=0
[pam_yubico.c:parse_cfg(731)] use_first_pass=0
[pam_yubico.c:parse_cfg(732)] authfile=(null)
[pam_yubico.c:parse_cfg(733)] ldapserver=(null)
[pam_yubico.c:parse_cfg(734)] ldap_uri=(null)
[pam_yubico.c:parse_cfg(735)] ldapdn=(null)
[pam_yubico.c:parse_cfg(736)] user_attr=(null)
[pam_yubico.c:parse_cfg(737)] yubi_attr=(null)
[pam_yubico.c:parse_cfg(738)] yubi_attr_prefix=(null)
[pam_yubico.c:parse_cfg(739)] url=(null)
[pam_yubico.c:parse_cfg(740)] capath=(null)
[pam_yubico.c:parse_cfg(741)] token_id_length=12
[pam_yubico.c:parse_cfg(742)] mode=chresp
[pam_yubico.c:parse_cfg(743)] chalresp_path=(null)
[pam_yubico.c:pam_sm_authenticate(775)] get user returned: jas
[pam_yubico.c:do_challenge_response(493)] Loading challenge from file /home/jas/.yubico/challenge-1077187
[util.c:load_chalresp_state(269)] Challenge: 23001a190724abf46c8022b008ccb65673dd634ecb150613771ec87f37850284d80dd5f8c8e56affb6da2e952b16682160e7f3ac4f816b64126bd9556e5be1, response: 63d4a679ed15335ffd4253e7609963bcdb0834d4, slot: 2
[pam_yubico.c:do_challenge_response(566)] Got the expected response, generating new challenge (63 bytes).
[pam_yubico.c:do_challenge_response(629)] Challenge-response success!

and if there is no YubiKey in the machine it will look like this:

[pam_yubico.c:parse_cfg(721)] called.
[pam_yubico.c:parse_cfg(722)] flags 32768 argc 2
[pam_yubico.c:parse_cfg(724)] argv[0]=mode=challenge-response
[pam_yubico.c:parse_cfg(724)] argv[1]=debug
[pam_yubico.c:parse_cfg(725)] id=-1
[pam_yubico.c:parse_cfg(726)] key=(null)
[pam_yubico.c:parse_cfg(727)] debug=1
[pam_yubico.c:parse_cfg(728)] alwaysok=0
[pam_yubico.c:parse_cfg(729)] verbose_otp=0
[pam_yubico.c:parse_cfg(730)] try_first_pass=0
[pam_yubico.c:parse_cfg(731)] use_first_pass=0
[pam_yubico.c:parse_cfg(732)] authfile=(null)
[pam_yubico.c:parse_cfg(733)] ldapserver=(null)
[pam_yubico.c:parse_cfg(734)] ldap_uri=(null)
[pam_yubico.c:parse_cfg(735)] ldapdn=(null)
[pam_yubico.c:parse_cfg(736)] user_attr=(null)
[pam_yubico.c:parse_cfg(737)] yubi_attr=(null)
[pam_yubico.c:parse_cfg(738)] yubi_attr_prefix=(null)
[pam_yubico.c:parse_cfg(739)] url=(null)
[pam_yubico.c:parse_cfg(740)] capath=(null)
[pam_yubico.c:parse_cfg(741)] token_id_length=12
[pam_yubico.c:parse_cfg(742)] mode=chresp
[pam_yubico.c:parse_cfg(743)] chalresp_path=(null)
[pam_yubico.c:pam_sm_authenticate(775)] get user returned: jas
[pam_yubico.c:do_challenge_response(478)] Failed initializing YubiKey
[pam_yubico.c:do_challenge_response(640)] Yubikey core error: no yubikey present