Two-Factor Authentication

Corey Steele

Issue #139, November 2005

With faster cracking programs available, passwords alone are no longer enough to keep naughty people off of your system. Use a USB device as a second check.

Two-factor authentication aims to solve the decades-old problem of password-based attacks, such as brute-force attacks and key-logging attacks. In Linux, two-factor authentication can be accomplished with pam_usb, a PAM module that provides a means by which you can authenticate against cryptographic tokens stored on removable media, such as a USB drive. Through the marvel of PAM's module chaining, this article walks you through configuring two-factor authentication.

PAM is short for pluggable authentication modules. According to the Linux-PAM home page:

PAM provides a way to develop programs that are independent of authentication scheme. These programs need authentication modules to be attached to them at run time in order to work. Which authentication module is to be attached is dependent upon the local system setup and is at the discretion of the local system administrator.

pam_usb is a PAM module written by Andrea Luzzardi that facilitates authentication from removable media, such as USB devices, based on strong cryptographic key pairs stored on the drive and on the system itself. pam_usb is available in source form or in binary packages for a variety of distributions, including Debian, Gentoo, Fedora, Mandrake and SUSE. pam_usb lends itself quite nicely to accomplishing two-factor authentication, although it can be used as the sole authentication module.

The term two-factor authentication refers to authentication being achieved using two separate and distinct criteria to authenticate a user's identity: usually this is something the user knows and something the user has. The something the user knows, in the configuration we're building, is the user name and password pair, while the something the user has is the strong cryptographic tokens we are going to generate and store on the USB drive.

Strictly speaking, you should be able to accomplish everything discussed here with any flavor of Linux that has a working PAM configuration and a 2.4 or newer kernel on a system with a supported USB controller. You also need a supported USB drive, the pam_usb module source and a C compiler.

I achieved everything discussed here with a Lexar 128MB Impact USB 1.1 drive on an IBM NetVista with an Intel 82820 Camino USB controller. It is running Debian 3.0 stable with the stock bf kernel (2.4) and gcc-2.3.

You can check to see if your controller and USB drive are supported by attaching your USB drive and running lsusb as root. If your controller and drive are supported, you should see the drive listed in the output of lsusb. If it isn't, don't despair; your distribution may not have auto-loaded the necessary modules. Consult The USB Guide (see the on-line Resources) for help getting your USB environment set up. Your PAM install can be confirmed by checking to see if your login program is linked against libpam by running ldd /bin/login | grep -i pam and checking the output. If login is linked against libpam, your PAM configuration should be set.

The source for the pam_usb module can be downloaded from the project site (see Resources). Use any browser to navigate the Web site and download the latest source tarball. Remember where you save the download. When the download is complete, uncompress the tarball with tar -zxvf pam_usb-X.Y.Z.tar.gz, where X, Y and Z are the major, minor and build versions, respectively, of the particular version of pam_usb you downloaded. You now should have a pam_usb-X.Y.Z directory, so cd into the directory and take a quick peek to make sure you have some files in the directory.

pam_usb does not have any configure scripts, only a Makefile, so building is simply a matter of running make from within the pam_usb-X.Y.Z directory. If you encounter errors, as I did, you probably are missing libraries. On my Debian 3.0 stable system, I was missing the development packages for libncurses5, libpam0g and libreadline4. Once I installed the missing libraries, the make completed without errors. After pam_usb builds, you can install it with make install as root from within the pam_usb-X.Y.Z directory.

After the installation is complete, it's time to configure pam_usb. Configuring pam_usb is a relatively straightforward task that can be broken in to three broad steps: creating the pam_usb log file, backing up your existing PAM configuration and installing the new configuration.

Creating the pam_usb log file is a matter of choosing where to put it and what to call it, as well as creating the file. My personal preference is to keep all logs in /var/log, so that's where I set up my pam_usb log file and that is the location used throughout this article. Create the log file with touch /var/log/pam_usb.log as root. Next, set the ownership of the /var/log/pam_usb.log file to match the ownership of other files in /var/log, like this:

chown $USER:$GROUP /var/log/pam_usb.log

where $USER and $GROUP are the user and group that own the other files in /var/log. Once the file has been created and ownership has been set, simply change the permissions on the file to reflect those of the other files in /var/log by using this command:

chmod 0600 /var/log/pam_usb.log

More advanced users may want to configure a log rotation schedule for the pam_usb.log or even change the file to be append-only with chattr. Those options are left as exercises for the reader to explore.

Now that the log file has been set up, we need to back up the existing PAM configuration files. This is an important step, so do not skip it. On most systems, the PAM configuration files are stored in /etc/pam.d. As root, make a backup copy with:

cp -rfp /etc/pam.d ~/pam.d/

For testing sake, we are working with the PAM configuration for su, because it is the easiest PAM-aware application to test. As a precautionary method, you should keep a root shell open and accessible so that if a mistake is made in configuring pam_usb, you are able to rescue yourself by overwriting the edited configuration files with backups from your ~/pam.d. You also need to know what filesystem is used on the USB drive(s) you will be configuring. In an ideal world, we can use mount to do the work for us, provided /mnt/usb exists and your USB drive is on /dev/sda. Use:

mount /dev/sda1 /mnt/usb

and then run:

mount | grep usb

to see what filesystem is on the drive—the filesystem is listed in parentheses at the end of the line. Most USB drives use the vfat filesystem and do not have more than one partition. Thus, they are mountable with:

mount -t vfat /dev/sda1 /mnt/usb

Our first real step in configuring pam_usb is to alter the PAM-aware applications' PAM configuration file—this step is required for each application you want to use pam_usb to authenticate to. Because we're working with su for testing purposes, focus only on the /etc/pam.d/su file. Do not try to configure every PAM-aware application in a single mass-edit of your /etc/pam.d directory, or tears and sorrow surely will be your lot. The files in /etc/pam.d/ correspond to the applications they configure, so if you were to configure console logins or GNOME Display Manager logins, you would be concerned with /etc/pam.d/login and /etc/pam.d/gdm, respectively. The naming pattern for PAM's configuration files should be relatively self-evident. So, open /etc/pam.d/su in your favorite text editor and add the following line above the pam_unix line:


auth required pam_usb.so fs=vfat check_device=-1 \
  check_if_mounted=-1 force_device=/dev/sda \
  log_file=/var/log/pam_usb.log

If you do not include the above line before the pam_unix line, PAM never reaches the point of authenticating against the USB device. Instead, it is satisfied by the authentication that occurs through pam_unix, and it drops out of the authentication process.

A few options in the pam_usb configuration that need further explanation: the force_device option, the pam_usb mode, the filesystem of the device and the log file we're going to use.

pam_usb is capable of autodetecting which USB-attached device houses the authentication keys. By not specifying the force_device directive, pam_usb walks through all of the attached devices and looks for keys matching the specified user name. This is helpful if the machine has multiple USB devices that are assigned device names according to the order in which they were attached—the first device is /dev/sda, the second is sdb and so on. If you specify the force_device directive, you are not able to authenticate unless your USB drive is assigned the device name specified in the PAM configuration.

pam_usb supports three modes of operation: unique, alternative and additional. With unique mode, you can log in using your USB drive, but if it's not present it isn't possible to log in. This is achieved by commenting out pam_unix in $PAMDIR/login and adding the configuration line above. The alternative mode allows you to log in simply by plugging in your USB key. If the key is not present, the system prompts for a password. This is accomplished by leaving pam_unix intact, adding the above configuration line to the PAM configuration file above the pam_unix entry and changing the auth required bits of the line to read auth sufficient. To achieve a true two-factor authentication, you need to require both the user name/password pair and the USB key, which is how the configuration above is set.

Andrea Luzzardi also points out an alternative two-factor authentication that involves encrypting the private key stored on the USB drive, after which the key requires a password to be decrypted and used for authentication. pam_usb is capable of passing the password provided to PAM through to decrypt the private key, thus accomplishing two-factor authentication off of a single user name and password pair. Furthermore, this is accomplished while not compromising any of the security benefits of having two-factor authentication. This method of authentication is contingent on using the same password for the user account that was used to encrypt the private key used by pam_usb. To encrypt the private key used by pam_usb, simply use the usbadm tool to create the cryptographic token:


usbadm cipher /path/to/usb/filesystem \
username algorithm

where the options have been specified according to the usbadm man page under cipher.

The fs= option tells pam_usb what filesystem to try to use to mount and read the USB drive. If your users have different filesystems on their USB drives, you'll have trouble with this. Simply specify whatever filesystem is used on your USB drives.

Once you've made the configuration changes to su's PAM configuration, it's time to set up a cryptographic key pair for each user using the system. Initially, this is done simply with:

usbadm keygen /path/to/mounted/usb/drive keysize

where keysize is the size (in bits) of the keys you want to generate and /path/to/mounted/usb/drive is the—you guessed it—path to the root of your mounted USB drive. For my setup, I chose a key size of 4,096 bits, which should be adequate to prevent even determined brute-force attempts against your key pair. RSA Labs recommends that DSA keys be no smaller than 2,048 bits, so at a minimum use a 2,048-bit key size. The usbadm program generates files in the root of your USB drive called .auth/$USER.$HOST, where $USER is the user name that executed the usbadm command and $HOST is the hostname of the machine on which the keys were generated. A corresponding set of keys in ~$USER/.auth must be present to authenticate with the USB token.

If a USB drive is lost, as is bound to happen, you can remove the user's ~/.auth/id_pub file and follow the instructions above to regenerate the key pair. Be certain you don't lose root's private keys or you'll have to boot to safe media, disable two-factor authentication and go through the whole setup process again to restore functionality.

Having freshly minted your key pair, you now are ready to test pam_usb and two-factor authentication with su. Insert your USB drive and try to su to a user who has a valid key pair; it's best to test this from a non-root account. You should be prompted for your user name as before, but instead of being prompted for your password immediately, you now should see a USB error as pam_usb tries to mount /dev/sda, or whatever base device you told it to try. Provided pam_usb was able to locate your USB drive, you should be prompted for the user's password, which if entered correctly, should result in a shell for that user account. You can make sure that the two-factor authentication worked by checking the pam_usb log file and verifying that somewhere near the last line is a line that reads Access granted. If you see that line in the pam_usb.log file, congratulations—su now is configured to use two-factor authentication.

Once you are satisfied with the functionality of pam_usb for su, you can duplicate the configuration for su with other applications that you want to set up with two-factor authentication. Be sure to issue all users the necessary keys and thoroughly test things before you log off the system and/or reboot.

As with any authentication system, two-factor authentication is not without its weaknesses. This particular implementation is vulnerable to private key theft, because it's easy to copy the contents of the USB drive. In the March 15, 2005, Crypto-Gram, Bruce Schneier writes a rather scathing article detailing why two-factor authentication is not the end-all-be-all of authentication—the crux of his point is that people are using two-factor authentication to achieve things it wasn't meant to achieve. With that in mind, remember that two-factor authentication is meant to address the age-old problems of password-based attacks. pam_usb achieves that end very well, and if properly configured, it can effectively improve the security of a given workstation.

Resources for this article: /article/8528.

Corey Steele is a security expert with six years of experience; he received CISSP certification in 2004. His primary interests in the security arena are access control and network security. He works in the financial sector for a company that makes core banking software. He has been an active member of the Free/Libre/Open Source Software community, having contributed to various projects, since 1995. In his spare time, he likes to write code and lecture on security topics.