--- lib/Makefile.am	2011-02-13 18:58:23.000000000 +0100
+++ lib/Makefile.am	2011-12-18 20:35:12.000000000 +0100
@@ -14,17 +14,19 @@
 	encrypt.c \
 	exitcodes.h \
 	faillog.h \
+	fprint.c \
+	fprint.h \
 	fputsx.c \
-	getdef.c \
-	getdef.h \
 	get_gid.c \
-	getlong.c \
 	get_pid.c \
 	get_uid.c \
+	getdef.c \
+	getdef.h \
+	getlong.c \
 	getulong.c \
 	groupio.c \
-	groupmem.c \
 	groupio.h \
+	groupmem.c \
 	gshadow.c \
 	lockpw.c \
 	nscd.c \
--- lib/fprint.c	1970-01-01 01:00:00.000000000 +0100
+++ lib/fprint.c	2011-12-18 21:57:56.000000000 +0100
@@ -0,0 +1,120 @@
+#include <sys/types.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "fprint.h"
+#include "getdef.h"
+
+static struct fp_print_data *data;
+static struct fp_dev        *handle;
+
+bool
+fprint_init(void)
+{
+	if (fp_init() != 0) {
+		fprintf(stderr, "couldn't initialize fprint\n");
+		return false;
+	}
+	return true;
+}
+
+void
+fprint_exit(void)
+{
+	fp_exit();
+}
+
+bool
+fprint_allowed(const char *user)
+{
+	struct group  *grp = getgrnam("fpauth");
+
+	char **members = grp->gr_mem;
+	char  *member = NULL;
+
+	while (member = *members++)
+		if (strcmp(user, member) == 0)
+			return true;
+
+	return false;
+}
+
+bool
+fprint_load(const char *user) {
+	struct passwd *pwd = getpwnam(user);
+	char          *oldhome = getenv("HOME");
+
+	struct fp_dscv_dev   **list = NULL;
+	struct fp_dscv_print **prints = NULL;
+
+	list = fp_discover_devs();
+	if (list != NULL && list[0] != NULL)
+		handle = fp_dev_open(list[0]);
+
+	/* list may also be NULL */
+	fp_dscv_devs_free(list);
+	list = NULL;
+
+	if (handle != NULL) {
+		/* same hack as in pam_fprint */
+		setenv("HOME", pwd->pw_dir, 1);
+		prints = fp_discover_prints();
+		/* clean up, just to be sure */
+		if (oldhome != NULL)
+			setenv("HOME", oldhome, 1);
+
+		if (prints != NULL && prints[0] != NULL) {
+			(void)fp_print_data_from_dscv_print(prints[0], &data);
+
+			fp_dscv_prints_free(prints);
+			prints = NULL;
+		}
+	}
+
+	return data != NULL;
+}
+
+void
+fprint_unload(void)
+{
+	fp_print_data_free(data);
+	fp_dev_close(handle);
+	data = handle = NULL;
+}
+
+bool
+fprint_auth(void)
+{
+	enum fp_verify_result result = FP_VERIFY_NO_MATCH;
+
+	do {
+		result = fp_verify_finger(handle, data);
+		switch (result) {
+		case FP_VERIFY_RETRY:
+			fprintf(stderr, "try again\n");
+			break;
+		case FP_VERIFY_RETRY_TOO_SHORT:
+			fprintf(stderr, "try again, slowly\n");
+			break;
+		case FP_VERIFY_RETRY_CENTER_FINGER:
+			fprintf(stderr, "try again, "
+				"finger centered on sensor\n");
+			break;
+		case FP_VERIFY_RETRY_REMOVE_FINGER:
+			fprintf(stderr, "try again, "
+				"removing finger from sensor afterwards\n");
+			/* FALLTHROUGH */
+		default:
+			break;
+		}
+	} while (result != FP_VERIFY_MATCH
+	      && result != FP_VERIFY_NO_MATCH);
+
+	return result == FP_VERIFY_MATCH;
+}
--- lib/fprint.h	1970-01-01 01:00:00.000000000 +0100
+++ lib/fprint.h	2011-12-18 21:58:05.000000000 +0100
@@ -0,0 +1,18 @@
+#ifndef _FPRINT_H_
+#define _FPRINT_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <libfprint/fprint.h>
+
+bool fprint_init(void);
+void fprint_exit(void);
+
+bool fprint_allowed(const char *);
+
+bool fprint_load(const char *);
+bool fprint_auth(void);
+void fprint_unload(void);
+
+#endif  /* !_FPRINT_H_ */
--- lib/getdef.c	2011-02-13 18:58:22.000000000 +0100
+++ lib/getdef.c	2011-12-18 20:35:12.000000000 +0100
@@ -62,6 +62,7 @@
 	{"ERASECHAR", NULL},
 	{"FAIL_DELAY", NULL},
 	{"FAKE_SHELL", NULL},
+	{"FPRINT_USER", NULL},
 	{"GID_MAX", NULL},
 	{"GID_MIN", NULL},
 	{"HUSHLOGIN_FILE", NULL},
--- lib/pwauth.c	2011-02-13 18:58:22.000000000 +0100
+++ lib/pwauth.c	2011-12-18 21:59:45.000000000 +0100
@@ -45,6 +45,7 @@
 #include "defines.h"
 #include "pwauth.h"
 #include "getdef.h"
+#include "fprint.h"
 #ifdef SKEY
 #include <skey.h>
 #endif
@@ -81,6 +82,9 @@
 	struct skey skey;
 #endif
 
+	bool fpallow = false;
+	bool fpavail = false;
+
 	/*
 	 * There are programs for adding and deleting authentication data.
 	 */
@@ -160,14 +164,27 @@
 #endif
 
 		snprintf (prompt, sizeof prompt, cp, user);
-		clear = getpass (prompt);
-		if (NULL == clear) {
-			static char c[1];
+		if (fpallow = fprint_allowed(user)) {
+			if (fpavail = fprint_load (user))
+				retval = !fprint_auth ();
+			fprint_unload ();
+		}
 
-			c[0] = '\0';
-			clear = c;
+		if (!fpallow || !fpavail) {
+			if (fpallow)  /* => !fpavail */
+				fprintf(stderr, "fingerprint authentication is "
+				    "not available, falling back to password "
+				    "authentication\n");
+			/* else expected password authentication */
+			clear = getpass (prompt);
+			if (NULL == clear) {
+				static char c[1];
+
+				c[0] = '\0';
+				clear = c;
+			}
+			input = clear;
 		}
-		input = clear;
 	}
 
 	/*
@@ -177,7 +194,8 @@
 	 * the results there as well.
 	 */
 
-	retval = strcmp (pw_encrypt (input, cipher), cipher);
+	if (!fpallow || !fpavail)
+		retval = strcmp (pw_encrypt (input, cipher), cipher);
 
 #ifdef  SKEY
 	/*
--- src/login.c	2011-02-13 18:58:16.000000000 +0100
+++ src/login.c	2011-12-18 20:35:12.000000000 +0100
@@ -78,6 +78,7 @@
 #define LASTLOG_FILE "/var/log/lastlog"
 #endif
 #endif				/* !USE_PAM */
+#include "fprint.h"
 
 /*
  * Global variables
@@ -628,6 +629,7 @@
 	OPENLOG ("login");
 
 	setup_tty ();
+	fprint_init ();
 
 #ifndef USE_PAM
 	(void) umask (getdef_num ("UMASK", GETDEF_DEFAULT_UMASK));
@@ -1306,6 +1308,7 @@
 		addenv ("HUSHLOGIN=TRUE", NULL);
 	}
 
+	fprint_exit();
 	ttytype (tty);
 
 	(void) signal (SIGQUIT, SIG_DFL);	/* default quit signal */
