summaryrefslogtreecommitdiff
path: root/pam
diff options
context:
space:
mode:
authorBenjamin Berg <bberg@redhat.com>2020-12-10 23:58:19 +0100
committerBenjamin Berg <benjamin@sipsolutions.net>2021-01-22 13:23:36 +0000
commit657f58fd648e35417ce7266b9c1558ce497dc179 (patch)
treeade27f44b95989ca557a73ae9890f940d2fdad3b /pam
parent584933aad7aa3de6c1edbe5c0d70a3963c3f3d2a (diff)
pam: Handle sigint by using a signalfd
It makes sense to allow interrupting fingerprint authentication, but PAM does not provide a way to define an interruptable operation. We can work around this somewhat though by at least reacting to SIGINT in an interactive terminal. Obviously, we shouldn't override the signal handler, because that would be too intrusive. But creating a signalfd is easy enough and doesn't affect the rest of the process state as much.
Diffstat (limited to 'pam')
-rw-r--r--pam/pam_fprintd.c44
1 files changed, 42 insertions, 2 deletions
diff --git a/pam/pam_fprintd.c b/pam/pam_fprintd.c
index 8dc68fd..1b40613 100644
--- a/pam/pam_fprintd.c
+++ b/pam/pam_fprintd.c
@@ -33,6 +33,9 @@
#include <libintl.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-login.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <poll.h>
#define PAM_SM_AUTH
#include <security/pam_modules.h>
@@ -59,6 +62,7 @@ static unsigned timeout = DEFAULT_TIMEOUT;
#define USEC_PER_SEC ((uint64_t) 1000000ULL)
#define NSEC_PER_USEC ((uint64_t) 1000ULL)
+#define USEC_PER_MSEC ((uint64_t) 1000ULL)
static size_t user_enrolled_prints_num (pam_handle_t *pamh,
sd_bus *bus,
@@ -368,6 +372,16 @@ verify_started_cb (sd_bus_message *m,
return 1;
}
+static void
+fd_cleanup (int *fd)
+{
+ if (*fd >= 0)
+ close (*fd);
+}
+
+typedef int fd_int;
+PF_DEFINE_AUTO_CLEAN_FUNC (fd_int, fd_cleanup);
+
static int
do_verify (sd_bus *bus,
verify_data *data)
@@ -375,6 +389,8 @@ do_verify (sd_bus *bus,
pf_autoptr (sd_bus_slot) verify_status_slot = NULL;
pf_autoptr (sd_bus_slot) verify_finger_selected_slot = NULL;
pf_autofree char *scan_type = NULL;
+ sigset_t signals;
+ fd_int signal_fd = -1;
int r;
/* Get some properties for the device */
@@ -425,6 +441,10 @@ do_verify (sd_bus *bus,
verify_finger_selected,
data);
+ sigemptyset (&signals);
+ sigaddset (&signals, SIGINT);
+ signal_fd = signalfd (signal_fd, &signals, SFD_NONBLOCK);
+
while (data->max_tries > 0)
{
uint64_t verification_end = now () + (timeout * USEC_PER_SEC);
@@ -459,12 +479,22 @@ do_verify (sd_bus *bus,
for (;;)
{
+ struct signalfd_siginfo siginfo;
int64_t wait_time;
wait_time = verification_end - now ();
if (wait_time <= 0)
break;
+ if (read (signal_fd, &siginfo, sizeof (siginfo)) > 0)
+ {
+ if (debug)
+ pam_syslog (data->pamh, LOG_DEBUG, "Received signal %d during verify", siginfo.ssi_signo);
+
+ /* The only way for this to happen is if we received SIGINT. */
+ return PAM_AUTHINFO_UNAVAIL;
+ }
+
r = sd_bus_process (bus, NULL);
if (r < 0)
break;
@@ -476,6 +506,11 @@ do_verify (sd_bus *bus,
break;
if (r == 0)
{
+ struct pollfd fds[2] = {
+ { sd_bus_get_fd (bus), sd_bus_get_events (bus), 0 },
+ { signal_fd, POLLIN, 0 },
+ };
+
if (debug)
{
pam_syslog (data->pamh, LOG_DEBUG,
@@ -483,8 +518,13 @@ do_verify (sd_bus *bus,
wait_time / USEC_PER_SEC,
wait_time);
}
- if (sd_bus_wait (bus, wait_time) < 0)
- break;
+
+ r = poll (fds, 2, wait_time / USEC_PER_MSEC);
+ if (r < 0 && errno != EINTR)
+ {
+ pam_syslog (data->pamh, LOG_ERR, "Error waiting for events: %d", errno);
+ return PAM_AUTHINFO_UNAVAIL;
+ }
}
}