Hacking Fingerprint GUI

Wolfgang Ullrich


Table of Contents

Debug Output
fingerprint-gui
fingerprint-identifier
fingerprint-helper
fingerprint-rw
fingerprint-plugin
pam_fingerprint-gui.so
Case 1 (username was given by keyboard)
Case 2 (user was identified by fingerprint)
Case 3 (user was known already; authentication)
Case 4 (user has typed the password)
Using the “try_first_identified” argument
Saving Passwords to Removable Media
Compiling the Sources

For developers who want to understand and possible change something in the Fingerprint GUI project it might be required to give some hints about how the system works. It is assumed the reader of this document has a certain understanding of PAM.

There are 6 executables and one library used:

  1. fingerprint-gui

    The main application to be used for discovering fingerprint scanners on the USB bus, acquiring (enrollment) and verification of fingerprints and testing the PAM settings.

  2. fingerprint-identifier

    An application for testing the identification of users by their fingerprints and to be used in special cases for any user-defined scripts in which a fingerprint identification is required.

  3. fingerprint-helper

    A helper application to be called out of PAM for requesting fingerprints while identifying or authenticating a user. This application should not be called from the command line.

  4. fingerprint-rw

    Another helper application to be called from “fingerprint-gui” and other modules when user settings and fingerprint (bir) data in “/var/lib/fingerprint-gui/” must be written or read.

  5. fingerprint-plugin

    A helper application to be plugged in into some other applications (like gnome-screensaver) that don't allow other GUI applications to be displayed on top of their own screen. This application should not be called from the command line.

  6. pam_fingerprint-gui.so

    A PAM module that is called by PAM when a user has to be identified or authenticated by their fingerprint.

  7. fingerprint-polkit-agent

    An authentication agent for policykit-1 with fingerprint recognition.

Debug Output

All modules accept a “debug” (or “-d” or “--debug”) argument to be given for execution. This argument causes a lot of debug output printed to syslog. Depending on syslog settings the output might be printed to “/var/log/auth.log”, “/var/log/messages” or “/var/log/syslog”.

fingerprint-gui

This software should be self explanatory. So no further information is needed at the moment. When running, a users manual is available in the help menu.

fingerprint-identifier

This program first tries to collect all stored fingerprint data of all users from user-specific directories in “/var/lib/fingerprint-gui/<username>/<drivername>/”. If a directory named after the user is available and can be read it collects all fingerprint data and tries to identify the user after a finger swipe was given. If it doesn't run with root permissions the directories of other users are not readable. In this case the fingerprints of the current user are accessible only. So only the current user can be identified or authenticated. After a successful authentication the user's login name is printed to stdout and the program exits.

fingerprint-helper

All Fingerprint GUI modules are developed using the Qt system, except the “pam_fingerprint-gui.so” module. This means the executables create a QApplication or a QCoreApplication object when running. Qt doesn't allow more than one of these objects to be created in an executable. Now, if some other Qt application would call PAM for authenticating a user and PAM would call the “pam_fingerprint-gui.so” library this would cause a crash, when “pam_fingerprint-gui.so” would try to create a (second) QApplication or QCoreApplication object while prompting for a finger swipe. Therefore pam_fingerprint-gui.so is not a Qt application but forks a child process (the fingerprint-helper) to prompt the user for a finger swipe and handle fingerprints.

fingerprint-rw

This application is called via two symlinks (fingerprint-rw-read and fingerprint-rw-write) and should not be called from the command line. Since version 1.07 files and directories in “/var/lib/fingerprint-gui/” are owned by root.root. Subdirectories have mode 655. Files have mode 600. For security reasons "fingerprint-rw" requests additional user authentication via polkit when creating subdirectories and writing files.

fingerprint-plugin

Some applications (namely "gnome-screensaver") don't accept for security reasons any GUI window to be displayed on top of their own screen. This means if gnome-screensaver calls PAM to prompt the user for authentication for the screensaver to be unlocked, the “pam_fingerprint-gui.so” module is called by PAM. This module forkes the “fingerprint-helper” process to request a finger swipe, but the window of this process is not visible (it is displayed “under” the locked screen). Therefore an additional “fingerprint-plugin” was created. This application is “plugged in” into the gnome-screensaver the same way an “embedded keyboard command” would be (by gnome-screensaver configuration).

When gnome-screensaver starts its unlock prompt, it starts the “fingerprint-plugin” module. This module plugs into the screensaver and is displayed below the normal unlock prompt. Then the “fingerprint-plugin” listens for “display commands” at a named pipe “/tmp/fingerprint-plugin”. The “fingerprint-helper” process, forked from “pam_fingerprint-gui.so” finds this named pipe and sends all strings to be displayed at the GUI to this pipe. If no fingerprints for the user are available a “stop” command is sent to the pipe that causes the “fingerprint-plugin” to exit before “fingerprint-helper” exits itself. If some other command has to be displayed to the user (e.g. “ready...” or “authenticating <username>”) this command will be received by “fingerprint-plugin” via it's named pipe and then displayed on its GUI window.

pam_fingerprint-gui.so

This is the PAM module to be called out of PAM in case some application requests authentication. When its “pam_sm_authenticate” entry is called the module determines whether DISPLAY and XAUTHORITY environment variables are available. If DISPLAY is available and XAUTHORITY is missing the module tries to find the xauthority filename by searching the command line of the X display process. If it was found “pam_fingerprint-gui.so” sets the  XAUTHORITY environment variable.

The module then creates an anonymous pipe and forkes into a child process. This process executes “fingerprint-helper” with pipe, display, PAM service to authenticate and username (if available) as arguments. After forking, a random number (10 digits) is sent to the child process via the anonymous pipe. This number will be given back (via the anonymous pipe) as a “password” if the user was identified or authenticated by his fingerprint. If the helper process has sent something to the pipe, it uses libfakekey or uinput to exit the “PAM conversation function”.

The parent process then calls the “PAM conversation function” of the calling application for prompting the username and/or password by keyboard while the child process (fingerprint-helper) prompts the user for swiping their finger.

Now the two processes wait for input in the following cases:

Case 1 (username was given by keyboard)

The PAM conversation function returns to the parent process (pam_fingerprint-gui.so) with a non empty username. This means the user did type his name on the keyboard. The helper process is stopped immediately by sending SIGUSR1 to it, the helper widget disappears and the user must type his password for login.

Case 2 (user was identified by fingerprint)

In this case the child process (fingerprint-helper) writes <enter> to the prompt using libfakekey or uinput and sends the username of the identified user via the pipe to his parent process (pam_fingerprint-gui.so). So the PAM conversation function returns an empty username field. Pam_fingerprint-gui.so then polls the fifo for a username and, if it can read it, sets this username to the PAM stack and sends SIGUSR1 to the helper. The helper then continues with case 3 below.

If there is nothing in the pipe to read, the user might have typed <enter> on the keyboard. In this case pam_fingerprint-gui.so sends SIGUSR2 to the helper to stop it immediately and returns PAM_AUTHINFO_UNAVAIL.

Case 3 (user was known already; authentication)

If the helper process has given the username to pam_fingerprint-gui.so via the pipe and receives SIGUSR1 it tries to get the users password from an encrypted file on the external media (USB stick, see 9. below). If available it sends this password via the pipe to pam_fingerprint-gui.so. If no “real” password is available it sends the random number to the pipe. Then the helper sends the <enter> key to the PAM prompt (via libfakekey or uinput) and exits. The same procedure happens, when the helper was forked with a known username from pam_fingerprint-gui.so.

Then the PAM module returns from its password prompt with an empty password. It polls the pipe for the password and compares this with the random number. If it matches, there was no “real” password available and the module returns PAM_SUCCESS. If it doesn't match it is the “real” password of this user. The PAM module then sets this password to the PAM stack and returns PAM_IGNORE. This causes the other PAM modules in the PAM stack to check the given username against this password.

Case 4 (user has typed the password)

If the PAM module returns from its password prompt with a non empty password field the user has typed his password at the prompt. Then the module sends SIGUSR2 to the helper to stop it immediately and returns PAM_IGNORE. So the other PAM modules can check the given username/password.

Using the “try_first_identified” argument

The pam_fingerprint-gui.so module allows to be called with the argument “try_first_identified”. This allows calling the module more then once in a given PAM stack without the effect to request a finger swipe more then once. If the user was successful identified/authenticated by his fingerprint in the first call of pam_fingerprint-gui.so the module saves this username to the PAM stack by a “pam_set_data” call. If pam_fingerprint-gui.so is then called a second time with the “try_first_identified” argument, it tries to read this username by a “pam_get_data” call. If there is a username available and this name matches the current username to be authenticated, it returns PAM_SUCCESS immediately.

Saving Passwords to Removable Media

With “fingerprint-gui” (“Password” Tab) the user can chose some directory on a mounted removable media, invoke his login password and save it to this media. This way the system can provide the login password to PAM while the user logs in with fingerprint to avoid a password request when e.g. gnome-keyring has to be opened or an encrypted home directory has to be mounted.

This login password information is split into 2 different locations:

  1. A file “<username>@<machinename>.xml” in a subdirectory “.fingerprints” on the chosen removable media, containing the encrypted password;

  2. A file “config.xml” in the directory “/var/lib/fingerprint-gui/<username>” containing the path to the “<username>@<machinename>.xml” file, the UUID of the chosen partition on removable media and the key for decrypting the password.

The password is encrypted with a random symmetric key (AES128-CBC-PKS7).

THERE IS A SECURITY RISK when this user is not the only one who has root access to the machine! Someone with root permission could connect to this machine (e.g. via ssh) and copy the “config.xml” file and the “<username>@<machinename>.xml” from the connected removable media and then decrypt the user's password.

When configured (see users manual) and the removable media is connected while fingerprint login, the system takes the following steps:

  1. After the user is identified (by fingerprint) the “fingerprint-helper” looks for the user's “/var/lib/fingerprint-gui/<username>/config.xml”  and, if found, reads the UUID, the decryption key and the initialization vector;

  2. Then creates a temporary directory “/tmp/<UUID>” and tries to mount the partition with this UUID there;

  3. Then reads the encrypted key from “<username>@<machinename>.xml” file and unmounts the media immediately;

  4. If the password can be decrypted it is given to pam_fingerprint-gui.so by an anonymous pipe;

  5. The pam_fingerprint-gui.so sets this password to PAM by a “pam_set_item()” call. If this call was successful it returns PAM_IGNORE. Then PAM calls the next module in stack (pam_unix.so) to validate username and password and complete the login process.

I'M NOT A CRYPTO EXPERT! If you are, please have a look at the sources (UserSettings.cpp) and let me know if there are possible problems.

Compiling the Sources

You need the Qt4 environment (incl. libqca2) and the “developer” packages of the used libraries installed on your system for being able to compile the sources. You can then use the “qmake-qt4” command to create the makefiles for your system. Then call “make” and it will create all executables in “./bin/...” subdirectories. After binaries are successful created you can use “sudo make install” to copy them to their proper locations. If you have a fingerprint scanner manufactured by UPEK inc. or SGS Thomson, you can install the bundled proprietary driver “libbsapi.so” by executing “sudo make install-upek”. This is needed because the open source driver (in libfprint) lacks the ability of comparing one-to-many fingerprints. Please have a look into the README file for more informations about the build options.

If you have fingerprint data created by a version 1.06 or below they have invalid ownership and permissions for version 1.07 or above. In this case you can execute “sudo make user-data” to setup correct ownership and permissions of the existing files and directories for version 1.07.