Locking and Unlocking a Linux Desktop Session with a Yubikey

Update:

This method of locking a user workstation with Yubikeys is now available for Gnome3, KDE, LXDE, Cinnamon and XFCE.

 

I, like many others use Linux as my main Operating System, both at home and in the enterprise. Token based authentication is also a very strong topic for many organisations for two factor authentication (password + one time password = successful login).

My preferred token of choice is the Yubikey from Yubico.

Questions have been floating around recently of “How do I lock my linux desktop when I unplug my Yubikey?”. I have been trying to achieve this for a few weeks now and despite coming across a fantastic article (http://wiki.d3xt3r01.tk/index.php/YubiKey_lock_screen), I never managed to get it to work in Fedora 18.

I’d like to just say here, that this is not my original work. Thank you very much to the author of the above link for their contribution to the community as it has helped many people to achieve this goal.

A special thanks to Lennart Poettering for pointing me in the right direction to get this working as well. Thanks very much.

Unfortunately for Gnome3 / Fedora 18 users, some changes needed to be made to the original method.
Below I will be walking you through setting up your Fedora 18 system running the Gnome3 desktop, to lock and unlock with your Yubikey.
(This will be based on the above article, however I have made a few changes to get things working for Fedora 18.)

Here is a short Youtube video of the final result.

http://www.youtube.com/watch?v=GyWfWUmdbVY

 

Step 1: Create a new udev rule.

Udev is used to map hardware to your system. We can tap into udev and ask it to do funky things for us, which is exactly what we need to do here.

Create a file called /etc/udev/rules.d/85-yubikey-screen-lock.rules with the below contents:

[root@localhost ~]# vi /etc/udev/rules.d/85-yubikey-screen-lock.rules 
SUBSYSTEM=="usb", ACTION=="remove", ENV{ID_VENDOR_ID}=="1050", ENV{ID_MODEL_ID}="0010", RUN+="/usr/local/bin/yubikey-screen-lock enable"
SUBSYSTEM=="usb", ACTION=="add", ENV{ID_VENDOR_ID}=="1050", ENV{ID_MODEL_ID}=="0010", RUN+="/usr/local/bin/yubikey-screen-lock disable"

Step 2. Create the yubikey-screen-lock

Create a file called /usr/local/bin/yubikey-screen-lock with the below contents:

[root@localhost ~]# vi /usr/local/bin/yubikey-screen-lock
#!/bin/bash
log="/var/log/yubilock.log"
yubimap="/etc/sysconfig/yubikeys"
user=`ps aux | grep -v root | grep session | head -n 1 | awk '{print $1}'`

check_gnome=$(ps -aux | awk '{print $11}' | grep ^gnome-session)
if [[ -n $check_gnome ]]
then
DESKTOP=gnome
sessionid=`/bin/loginctl list-sessions | grep ${user} | awk '{print $1}'`
fi

check_cinnamon=$(ps -aux | awk '{print $11}' | grep ^cinnamon-session)
if [[ -n $check_cinnamon ]]
then
DESKTOP=cinnamon
fi

check_lxde=$(ps -aux | awk '{print $11}' | grep lxsession)
if [[ -n $check_lxde ]]
then
DESKTOP=lxde
fi

check_xfce=$(ps -aux | awk '{print $11}' | grep ^xfce4-session)
if [[ -n $check_xfce ]]
then
DESKTOP=xfce
fi

check_kde=$(ps -aux | awk '{print $11}' | grep kded4)
if [[ -n $check_kde ]]
then
DESKTOP=kde
fi

echo "$(date) $(whoami) '$0' '$1' | Desktop Environment = '$DESKTOP'" >> ${log}
case "$1" in
        enable)
                if [ -n ${user} -a "$(grep -c ${user}:000$(ykinfo -q -s) ${yubimap})" == "1" ]
                then
                case $DESKTOP in
                        gnome)
                                /bin/loginctl lock-session $sessionid
                        ;;
                        cinnamon)
                                /bin/su ${user} -c "DISPLAY=:0 /usr/bin/cinnamon-screensaver-command -a"
                        ;;
                        lxde)
                                /bin/su ${user} -c "DISPLAY=:0 /usr/bin/xscreensaver-command -activate"
                        ;;
                        xfce)
                                /bin/su ${user} -c "DISPLAY=:0 /usr/bin/xflock4"
                        ;;
                        kde)
                                /bin/su ${user} -c "DISPLAY=:0 /usr/bin/xscreensaver-command -activate"
                        ;;
                esac
                fi
        ;;
        disable)
                if [ -n ${user} -a "$(grep -c ${user}:000$(ykinfo -q -s) ${yubimap})" == "1" ]
                then
                case $DESKTOP in
                        gnome)
                                /bin/loginctl unlock-session $sessionid
                        ;;
                        cinnamon)
                                /bin/su ${user} -c "DISPLAY=:0 /usr/bin/cinnamon-screensaver-command -d"
                        ;;
                        lxde)
                                /bin/su ${user} -c "DISPLAY=:0 /usr/bin/xscreensaver-command -deactivate"
                        ;;
                        xfce)
                                /bin/su ${user} -c "DISPLAY=:0 /usr/bin/xscreensaver-command -deactivate"
                        ;;
                        kde)
                                /bin/su ${user} -c "DISPLAY=:0 /usr/bin/xscreensaver-command -deactivate"
                        ;;
                esac
                fi
        ;;
esac

Step 3. Ensure yubikey-screen-lock can be executed

Here we need to change the permissions of the /usr/local/bin/yubikey-screen-lock script to allow it to execute like any other normal command.

Please do the following:

[root@localhost ~]# chmod 755 /usr/local/bin/yubikey-screen-lock

Step 4. Add username and yubikey token to authorised file

We need to create a user maps file which has a list of which Yubikeys can unlock which user’s sessions. We need to do this to stop user Jim, from unlocking user Bob’s session.

Firstly, insert a single Yubikey into your system and on your terminal, run “ykinfo -q -s”. This will give you the output of your key serial number.

For example:
This is one of my test keys.

[root@localhost ~]# ykinfo -q -s
283749

Now we need to take this number, and add it to our user maps file. Here the syntax is <username>:000<serial number>

For example:
Here my username is mac and my serial number is 283749, so my user maps file looks as follows.

[root@localhost ~]# cat /etc/sysconfig/yubikeys
mac:000283749
[root@localhost ~]#

Step 5. Reload udev

As we have just added a new rule, we need to apply it so udev knows to action it.
To reload udev in Feodra 18, run the following.

[root@localhost ~]# udevadm control --reload-rules

Step 6. Test

It is important to test your configuration. If you are making these chances and are already logged into Gnome3 as the user you have set up, simply by removing your Yubikey will activate the lock screen.

If you reinsert your key, it will take you back to your existing session.

 

Please note:
One of the big reasons Two factor authentication is seen as so critical is because it forces a user to require both a password AND a key or token to log back into your session.

If you are planning to do this in the enterprise, I highly recommend that you do not unlock your sessions with your Yubikey tokens.

If you use systems in a kerberised environment (for example Microsoft Active Directory or FreeIPA), unlocking your user session will not acquire a valid authentication ticket (TGT). If you don’t acquire a ticket, you won’t be able to access several resources that would normally be required.

My recommendation here is, only use this in the enterprise to lock your sessions.

To achieve this, change your udev rule from:

[root@localhost ~]# vi /etc/udev/rules.d/85-yubikey-screen-lock.rules 
SUBSYSTEM=="usb", ACTION=="remove", ENV{ID_VENDOR_ID}=="1050", ENV{ID_MODEL_ID}="0010", RUN+="/usr/local/bin/yubikey-screen-lock enable"
SUBSYSTEM=="usb", ACTION=="add", ENV{ID_VENDOR_ID}=="1050", ENV{ID_MODEL_ID}=="0010", RUN+="/usr/local/bin/yubikey-screen-lock disable"

to

[root@localhost ~]# vi /etc/udev/rules.d/85-yubikey-screen-lock.rules 
SUBSYSTEM=="usb", ACTION=="remove", ENV{ID_VENDOR_ID}=="1050", ENV{ID_MODEL_ID}="0010", RUN+="/usr/local/bin/yubikey-screen-lock enable"
#SUBSYSTEM=="usb", ACTION=="add", ENV{ID_VENDOR_ID}=="1050", ENV{ID_MODEL_ID}=="0010", RUN+="/usr/local/bin/yubikey-screen-lock disable"

Don’t forget to reload your rules after you make this change.

[root@localhost ~]# udevadm control --reload-rules

 

Troubleshooting

If you are having problems with your new udev rule, perhaps you have hit a problem with SELinux if you use it in enforcing mode.

 

Firstly, verify if SELinux is causing a problem.
You can do this by setting SELinux to permissive mode whilst you troubleshoot. To do this, run the following

[root@localhost ~]# setenforce 0

Now insert and remove your yubikey. If your GNOME3 session now locks and unlocks with no problems then chances are you are affected by the same problem I had.

When I tailed my /var/log/audit/audit.log file, I found the following kept appearing when I inserted my key.

type=USER_AVC msg=audit(1358202964.902:412): pid=563 uid=81 auid=4294967295 ses=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc:  denied  { send_msg } for msgtype=method_return dest=:1.106 spid=548 tpid=2305 scontext=system_u:system_r:systemd_logind_t:s0 tcontext=system_u:system_r:udev_t:s0-s0:c0.c1023 tclass=dbus  exe="/usr/bin/dbus-daemon" sauid=81 hostname=? addr=? terminal=?'

 

What I did to resolve the issue was,

Save the above output to a text file. I called mine selinux.txt

I then cat’d the file and passed it to ‘audit2allow’ which is a tool you can use to create your own SELinux policies.

[root@localhost ~]# cat selinux.txt | audit2allow -M yubikey_session_lock
******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i yubikey_session_lock.pp

[root@localhost ~]#

 

Next you should install your new policy with the following.

[root@localhost ~]# semodule -i yubikey_session_lock.pp

Next, just reenable SELinux and try testing your keys again.

Turn SELinux back to Enforcing mode with the following.

[root@localhost ~]# setenforce 1

 

 

13 comments on “Locking and Unlocking a Linux Desktop Session with a Yubikey

  1. James January 26, 2013 10:22

    I came across this script since I was looking to lock / unlock a gnome 3 session under fedora 18 using a yubikey. Seems ideal 🙂 However, it appears that the script has commented out the checks to see which yubikey has been inserted, so basically it will lock / unlock a session if any yubikey is removed / inserted (which isnt quite what I want, although no-one else should be using my laptop it would be safer to make sure). Is there any reason for the checks to be commented out?

    • Dale Macartney January 26, 2013 11:18

      Hi James

      Thanks for pointing that out. I have just removed the #’s in the script. If you remove the #’s as well it will validate the key to user match in the mappings file.

      Looks like I had them left over from when I was testing the raw functionality with udev.

      Thanks again for noticing.

      Dale

  2. wayne November 27, 2013 20:49

    Hi, is this possible to do with KDE?

    • Dale Macartney November 29, 2013 12:54

      Hi Wayne
      I have just added support for new Desktop Environments, which includes support for KDE.

      I have tested this on Fedora 19/20 so please feel free to give it a go.

      Dale

  3. Jacco Landlust May 12, 2014 10:32

    Just implemented this on ubuntu 14.04 with gnome (unity), had to implement 2 small changes:
    1. /etc/sysconfig does not exist on ubunty (step 4 in your manual). I just placed the yubimap directly in /etc. Also I changed privileges to 600 on the yubimap file (for security)
    2. /bin/loginctl list-sessions can return multiple rows. This causes the script to stop working. Therefore I setup lock and unlock in a loop:

    for sessionid in `/bin/loginctl list-sessions | grep ${user} | awk ‘{print $1}’`
    do
    /bin/loginctl lock-session $sessionid
    done

    Hope this helps.

  4. Brett June 3, 2014 17:25

    Thanks for the writeup, i simplified the lock script:

    #!/bin/bash
    log="/var/log/yubilock.log"
    yubimap="/etc/yubikeys"
    user=`ps aux | grep -v root | grep session | head -n 1 | awk '{print $1}'`

    sessionid=`/bin/loginctl list-sessions | grep ${user} | awk '{print $1}'`

    echo "$(date) $(whoami) '$0' '$1' | Desktop Environment = '$DESKTOP'" >> ${log}
    /bin/loginctl lock-session $sessionid

    and the udev rule (since fedora 20 works well with yubikey):


    SUBSYSTEM=="usb", ACTION=="remove", ENV{ID_VENDOR_ID}=="1050", ENV{ID_MODEL_ID}="0010", RUN+="/usr/local/bin/yubilock"

  5. melbo July 8, 2014 22:41

    This is mostly working for me on Fedora 20 w/ Gnome. However, if I insert a second yubikey (with different serial # and unmapped in user maps) and remove it, it also locks the screen. For whatever reason, it doesn’t seem to be respecting the user map file and is just seeing a yubikey and locking on removal. This happens with and without SELinux. Any ideas?

  6. melbo July 8, 2014 22:46

    Fixed. I realized that my yubikey Serial Number is 1234567 whereas your example was 123456 so in my case (and anyone else with a larger yubikey SN) the syntax would be
    :00

    Working flawlessly now.

    • melbo July 8, 2014 22:53

      Oops, delete my ‘fix’. it worked until a reboot and now it doesn’t work at all. Sorry 🙂

  7. Tim F January 28, 2015 10:28

    A couple of extra tweaks I have added to the script are:

    pkill -u ${user} -SIGHUP gpg-agent
    if which sudo >/dev/null ; then sudo -H -u ${user} sudo -K ; fi

    The first line tells gpg-agent (which I use with my yubikey to mange ssh keys) to drop it’s password cache

    The second line tells sudo to drop my credentials cache.

    The net effect is to drop all my password caches as I leave my workstation.

  8. Greg Swift April 12, 2015 04:31

    I’m playing around with this and its pretty kewl. I would suggest adding ‘loginctl activate $sessionid’ for those that are playing around with actually allowing this to unlock them

  9. Daniel February 8, 2016 21:23

    Great post !
    I’m using XFCE and I had to replace /bin/su ${user} -c “DISPLAY=:0 /usr/bin/xflock4” with export XDG_SEAT_PATH=”/org/freedesktop/DisplayManager/Seat0″; /usr/bin/dm-tool lock in order to get this working.

  10. Finn Glöe February 22, 2016 12:18

    Thanks for this post. It helped me a lot. I just had to replace the model id for the Yubikey Neo by ENV{ID_MODEL_ID}=”0116″ and to apply the following patch for Arch Linux with Gnome.

    4c4
    user=`ps aux | grep -v gdm | grep session | head -n 1 | awk ‘{print $1}’`
    6c6
    check_gnome=$(ps -aux | awk ‘{print $11}’ | grep gnome-session)

Leave a Reply to Dale Macartney Cancel Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>