Yubikey required at boot

Update (02/11/2012) I added the ‘ask a passphrase’ functionnality in the hook.

Intro

As you might already know, I have a yubikey I use as an authentication token. Without it, I cannot log on my computer as a normal user.

But I wanted to do more than that. Like, blocking the boot if the key is not present, unmounting encrypted drive by removing the key, etc.

In this post, I’ll show you how I’ve tweaked my initrd system to stop booting if I haven’t plugged in the key. I’m using the basic kernel from arch linux, and the mkinitcpio system that is shipped in this distribution.

However, the scripts mught be easy to port to a different one.

Writing hooks

I needed a new hook for that. This hook will be responsible of embedding the necessary binaries and modules, and to run them at boot.

The Arch wiki has a page about writing some custom hooks. It just need two non-executable scripts. The neat thing is that those script will embedd all required dependencies when creating the image.

So, use your editor of choice and create the first file /usr/lib/initcpio/hooks/yubikey and paste this content in it:

\#!/bin/bash  \# Use y2kchalresp to test if the yubikey is present run\_hook() {     local CHAL YCHAL PASS TRIES OK     msg ":: Loading necessary modules for yubikey..."     /sbin/modprobe hid\_generic      sleep 2

First, we need to load the required modules. dmesg tolds me that this is the module hid_generic (quite expectable since the key actually is a usb keyboard). I need to sleep a little bit, to give time to the USB bus to detect the key. In case your system doesn’t detect the key, you might need to increase it.

    TRIES=0     OK="KO"     CHAL="thechallengeresult"     while [ $TRIES -lt 3 ]     do         read -p "Enter your yubikey passphrase: " -s PASS         YCHAL=$(ykchalresp -2 "$PASS")

This is the crypto part of it. CHAL contains the expected result challenge (that is the result of the command runned in YCHAL), the PASS is the challenge submitted to the key and YCHAL is the command sent to the key to have an answer from it.

We also start a loop to grants you the ability to mistype your password. The call to read with the -s flag is used to define a passphrase and to not display what you’re typing.

        if [ "$CHAL" != "$YCHAL" ]         then             err "Challenge Response with yubikey failed"             ((TRIES += 1))         else             msg "Challenge Response with yubikey correct"             OK="OK"             break         fi     if [ "$OK" != "OK ]     then         exit 1     fi }

If everything is ok, CHAL and YCHAL are equals, and you can process to the end of the boot. Else, you increment TRIEs, and you loop. If tries is greater or equal to 3, then you end the loop.

At the end of the loop, if OK doesn’t contain OK, then exit, else continue the normal boot process.

The second needed file require by mkinitcpio, in the /usr/lib/initcpio/install/yubikey script.

#!/bin/bash  build() {     add_module hid_generic     add_binary /usr/bin/ykchalresp     add_runscript }

The build function is called to pack everything in the initrd. We need a module and a binary, so we add them here. And then the add_runscript function tells mkinitcpio that there is a script in hooks/yubikey to be included.

help() { cat <<HELPEOF     This hook tries to lock the computer at boot if no yubikey is inserted HELPEOF }

The help function just display a message when you want to know what this hook is about.

Then, just add the yubikey hook in your HOOKS array, edit /etc/mkinitcpio.conf and add it after the usbinput things.

And rebuild the initrd.

mkinitcpio -p linux

And now, on boot, you will need your yubikey plugged in.