diff options
author | Benjamin Berg <bberg@redhat.com> | 2020-12-10 23:58:19 +0100 |
---|---|---|
committer | Benjamin Berg <benjamin@sipsolutions.net> | 2021-01-22 13:23:36 +0000 |
commit | 657f58fd648e35417ce7266b9c1558ce497dc179 (patch) | |
tree | ade27f44b95989ca557a73ae9890f940d2fdad3b /pam | |
parent | 584933aad7aa3de6c1edbe5c0d70a3963c3f3d2a (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.c | 44 |
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; + } } } |