U2F Authentication on OS X

I recently bought a U2F Zero, an $8.00 open source U2F (Universal 2nd Factor) hardware key.

After the initial setup of the U2F Zero (hint: there was none), I immediately wondered how I could enable it as a required second factor when logging into my Mac.

After a bit of browsing on Yubico's site and a couple of blogs, I didn't find a solid step-by-step tutorial on how to set this up that fully described the process and the different configurations. So here we are.

Note: These instructions apply for any U2F token, not just the U2F Zero or YubiKey devices.

If you're feeling dangerous (and I really do mean that), you can skip to the very end and just see which commands to run.

U2F Zero

The U2F Zero is an open source DIY (or purchase pre-built from Amazon) hardware token that supports U2F (Univeral 2nd Factor) authentication. At $8.00, it's hard to beat the price, and similar products like the various YubiKeys aren't open source, which is a minor inconvenience for some and a dealbreaker for others.

You have the option of either purchasing it pre-assembled from Amazon or building it yourself. While I always enjoy soldering, the programmer alone is around $35, so unless you're going to build quite a few, you might be best off just buying them pre-built.

Enabling 2FA for OS X

Note: I'm doing this on macOS Sierra (10.12), but this should work on any recent version of OS X.

Basically I am trying to achieve the following: when logging in either after a restart or upon waking from sleep, I would like to require a U2F token as a second authentication factor, in addition to my password.

The process is actually extremely simple, and boils down to:

  1. Install the pam-u2f module
  2. Generate the Authorization Mapping File for your key(s)
  3. Edit the system configuration files to enable U2F authentication

1. pam-u2f Module

Installing the pam-u2f module is easiest with Homebrew, in which case a simple

brew install pam-u2f  

will suffice.

If you're not using Homebrew on OS X (seriously?), you can build it from source following the instructions here. You will also need autoconf and automake and friends, and also libu2f-host and libu2f-server.

In either case, you will end up with a shared library called pam_u2f.so. If you installed via Homebrew (be sure to check the version since the path will change) it will be located at /usr/local/Cellar/pam-u2f/1.0.4/lib/pam/pam_u2f.so.

To make the configuration step easier, you can symlink the Homebrew library or copy the library you built into /usr/lib/pam/.

cd /usr/lib/pam  
sudo ln -s /usr/local/Cellar/pam-u2f/1.0.4/lib/pam/pam_u2f.so

# Or copy if you built it yourself

Note: You will get an Operation not permitted message on OS X El Capitan or macOS Sierra, since you must disable System Integrity Protection to have write access to anything in /usr other than /usr/local. You can disable SIP by booting into recovery mode (⌘ + R) and then opening a Terminal and executing csrutil disable.

If you decide not to disable SIP or just not to link or copy into /usr/lib/pam, you can also just provide the full path to the pam_u2f.so file in the configuration files later.

2. Authorization Mapping Files

In order for the module to know which U2F token to accept the challenge response from and authenticate with, you must store some information about your token. Specifically, you must create what is called an Authorization Mapping File, which contains a list of token pairs consisting of the key handles and the public keys.

More information on these files can be found here.

To generate this file, you can use the pamu2fcfg utility which is installed with the pam-u2f recipe on Homebrew or can also be built manually.

Insert your U2F token and then open a Terminal and run

pamu2fcfg  

Note: If you want to be verbose about the username for the token you can add the -u <username> option.

If the device is inserted correctly, the colour of the light on the U2F Zero will change, prompting you to press the button to complete the challenge response. If everything worked you will see some output on the screen consisting of

<username>:<key handle>,<public key>\n  

I'm going to place this in a file called ~/.config/u2f/keys, but you can place it anywhere. To directly output to the file:

# Create the directory if it doesn't exist
mkdir -p ~/.config/u2f  
# Use >> if you want to append, or a single > if you want to rewrite the file
pamu2fcfg > ~/.config/u2f/keys  

You'll need to specify this location in the PAM configuration files, unless you place it in the file ~/.config/Yubico/u2f_keys, which is the default search location.

Multiple U2F Tokens

To use multiple tokens under the same user on the same machine, you just need to concatenate the second token's key information to the mapping file.

With one user and two tokens, the mapping file should look like:

<username>:<key handle 1>,<public key 1>:<key handle 2>,<public key 2>  

Thankfully, the PAM U2F config tool includes an option (-n) to leave off the username, so in the simple case of one user and two tokens you can just do

pamu2fcfg -n >> ~/.config/u2f/keys  

with the second token.

If, however, you wanted to add a second token but for a different user, you would just add a separate line with the output right from pamu2fcfg, no modification necessary.

For multiple users and/or tokens the mapping file could look like:

<username 1>:<key handle 1>,<public key 1>:<key handle 2>,<public key 2>  
<username 2>:<key handle 3>,<public key 3>:<key handle 2>,<public key 2>  

In that example, both users have a unique token (keys 1 and 3) and also share a token (key 2).

3. PAM Configuration Files

The PAM configuration files are located at /etc/pam.d.

You will see a list of configuration files for various authentication modules here. Of these files, the screensaver and authorization files are of particular interest to us.

Note: In these steps it is extremely important to use the sufficient keyword and not the required keyword when first configuring these. This gives you the opportunity to test it out and verify that you can correctly use your U2F token to log in.

Open up the screensaver file (with sudo, of course) and add a line consisting of:

auth       sufficient     <path to pam_u2f.so> authfile=<path to mapping file>  

Using the full Homebrew path and placing my authorization mapping file under ~/.config/u2f/keys, my line looks like:

auth       sufficient     /usr/local/Cellar/pam-u2f/1.0.4/lib/pam/pam_u2f.so authfile=/Users/joe/.config/u2f/keys  

Note: You cannot perform shell expansions (e.g. ~) here so give the full path.

If you symlinked or installed directly into /usr/lib/pam, and if you created your mapping file under ~/.config/Yubico/u2f_keys, you can simply use:

auth       sufficient     pam_u2f.so  

Then save and exit. Make sure you're configured to require a password immediately after going to sleep, and then tell your Mac to go to sleep.

Upon waking, insert your U2F token and then type your password. After entering the password, it should pause and the light on your U2F Zero should change colours. Et voilà! Press the button to complete the challenge response and you should be successfully logged in with your U2F as a second factor!

You can now perform the next step by adding the same line to the authorization file, and then log out and back in to test.

Since we chose sufficient for the test, if you don't have your token inserted when you type your password, it will log in without asking for it. To require your token in addition to your password for true 2FA, simply change the sufficient keyword to required in the authorization and screensaver files.

Note: It is a very good idea to first try the required configuration with the screensaver file only, since if something goes wrong, you can still authenticate via the normal login screen without your token.

As mentioned, be careful with requiring the token. If you lose your token, you may be totally locked out of your Mac. One solution to this is to buy two tokens and add both of them to your authorization mapping file. Then store that second token away for safe keeping.

Commands

If you really truly don't think you need to read about what it is that you're doing, you can achieve all of this by just running the following commands:

# Install PAM U2F Module
brew install pam-u2f

# Generate a new authorization mapping file
mkdir -p ~/.config/u2f  
# >>> Insert U2F Token
pamu2fcfg > ~/.config/u2f/keys  
# >>> Press button on U2F token
# >>> Remove token
# >>> Insert second U2F token (if you have one)
pamu2fcfg -n >> ~/.config/u2f/keys  
# >>> Press button on second U2F token
# >>> Remove token
# ... Repeat for each additional token

# Edit PAM configuration files
sudo $EDITOR /etc/pam.d/screensaver  
### Add a line with:
# auth       required     <path to pam_u2f.so> authfile=<path to mapping file>

sudo $EDITOR /etc/pam.d/authorization  
### Add a line with:
# auth       required     <path to pam_u2f.so> authfile=<path to mapping file>