Block SIGINT and SIGQUIT while verifying passwords so that

authentication modules that use sleep() are not interrupted.
If the user interrupted authentication, exit the loop.
This commit is contained in:
Todd C. Miller
2014-09-27 10:16:31 -06:00
parent 0660d88b2b
commit 355834c76a
2 changed files with 74 additions and 15 deletions

View File

@@ -43,6 +43,7 @@
# include <unistd.h> # include <unistd.h>
#endif /* HAVE_UNISTD_H */ #endif /* HAVE_UNISTD_H */
#include <pwd.h> #include <pwd.h>
#include <signal.h>
#include <siad.h> #include <siad.h>
#include "sudoers.h" #include "sudoers.h"
@@ -62,6 +63,8 @@ static int
sudo_collect(int timeout, int rendition, uchar_t *title, int nprompts, sudo_collect(int timeout, int rendition, uchar_t *title, int nprompts,
prompt_t *prompts) prompt_t *prompts)
{ {
int rval;
sigset_t mask, omask;
debug_decl(sudo_collect, SUDO_DEBUG_AUTH) debug_decl(sudo_collect, SUDO_DEBUG_AUTH)
switch (rendition) { switch (rendition) {
@@ -82,7 +85,18 @@ sudo_collect(int timeout, int rendition, uchar_t *title, int nprompts,
break; break;
} }
debug_return_int(sia_collect_trm(timeout, rendition, title, nprompts, prompts)); /* Unblock SIGINT and SIGQUIT during password entry. */
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
sigprocmask(SIG_UNBLOCK, &mask, &omask);
rval = sia_collect_trm(timeout, rendition, title, nprompts, prompts);
/* Restore previous signal mask. */
sigprocmask(SIG_SETMASK, &omask, NULL);
debug_return_int(rval);
} }
int int
@@ -118,7 +132,7 @@ sudo_sia_verify(struct passwd *pw, char *prompt, sudo_auth *auth)
def_prompt = prompt; /* for sudo_collect */ def_prompt = prompt; /* for sudo_collect */
/* XXX - need a way to detect user hitting return or EOF at prompt */ /* XXX - need a way to detect user hitting ^C or EOF at prompt */
if (sia_ses_reauthent(sudo_collect, siah) == SIASUCCESS) if (sia_ses_reauthent(sudo_collect, siah) == SIASUCCESS)
debug_return_int(AUTH_SUCCESS); debug_return_int(AUTH_SUCCESS);
else else

View File

@@ -174,6 +174,15 @@ pass_warn(void)
debug_return; debug_return;
} }
static bool
user_interrupted(void)
{
sigset_t mask;
return (sigpending(&mask) == 0 &&
(sigismember(&mask, SIGINT) || sigismember(&mask, SIGQUIT)));
}
/* /*
* Verify the specified user. * Verify the specified user.
* Returns true if verified, false if not or -1 on error. * Returns true if verified, false if not or -1 on error.
@@ -186,15 +195,10 @@ verify_user(struct passwd *pw, char *prompt, int validated)
int status, rval; int status, rval;
char *p; char *p;
sudo_auth *auth; sudo_auth *auth;
sigaction_t sa, osa; sigset_t mask, omask;
sigaction_t sa, saved_sigtstp;
debug_decl(verify_user, SUDO_DEBUG_AUTH) debug_decl(verify_user, SUDO_DEBUG_AUTH)
/* Enable suspend during password entry. */
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
(void) sigaction(SIGTSTP, &sa, &osa);
/* Make sure we have at least one auth method. */ /* Make sure we have at least one auth method. */
if (auth_switch[0].name == NULL) { if (auth_switch[0].name == NULL) {
audit_failure(NewArgc, NewArgv, N_("no authentication methods")); audit_failure(NewArgc, NewArgv, N_("no authentication methods"));
@@ -205,9 +209,33 @@ verify_user(struct passwd *pw, char *prompt, int validated)
debug_return_int(-1); debug_return_int(-1);
} }
/* Enable suspend during password entry. */
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
(void) sigaction(SIGTSTP, &sa, &saved_sigtstp);
/*
* We treat authentication as a critical section and block
* keyboard-generated signals such as SIGINT and SIGQUIT
* which might otherwise interrupt a sleep(3).
* They are temporarily unblocked by auth_getpass().
*/
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
(void) sigprocmask(SIG_BLOCK, &mask, &omask);
while (--counter) { while (--counter) {
int num_methods = 0; int num_methods = 0;
/* If user attempted to interrupt password verify, quit now. */
if (user_interrupted())
goto done;
if (counter != def_passwd_tries)
pass_warn();
/* Do any per-method setup and unconfigure the method if needed */ /* Do any per-method setup and unconfigure the method if needed */
for (auth = auth_switch; auth->name; auth++) { for (auth = auth_switch; auth->name; auth++) {
if (IS_DISABLED(auth)) if (IS_DISABLED(auth))
@@ -217,7 +245,7 @@ verify_user(struct passwd *pw, char *prompt, int validated)
status = (auth->setup)(pw, &prompt, auth); status = (auth->setup)(pw, &prompt, auth);
if (status == AUTH_FAILURE) if (status == AUTH_FAILURE)
SET(auth->flags, FLAG_DISABLED); SET(auth->flags, FLAG_DISABLED);
else if (status == AUTH_FATAL) else if (status == AUTH_FATAL || user_interrupted())
goto done; /* assume error msg already printed */ goto done; /* assume error msg already printed */
} }
} }
@@ -244,18 +272,23 @@ verify_user(struct passwd *pw, char *prompt, int validated)
continue; continue;
success = auth->status = (auth->verify)(pw, p, auth); success = auth->status = (auth->verify)(pw, p, auth);
if (auth->status != AUTH_FAILURE) if (success != AUTH_FAILURE)
goto done; break;
} }
if (!standalone) if (!standalone)
memset_s(p, SUDO_CONV_REPL_MAX, 0, strlen(p)); memset_s(p, SUDO_CONV_REPL_MAX, 0, strlen(p));
pass_warn();
if (success != AUTH_FAILURE)
goto done;
} }
done: done:
/* Restore signal handlers and signal mask. */
(void) sigaction(SIGTSTP, &saved_sigtstp, NULL);
(void) sigprocmask(SIG_SETMASK, &omask, NULL);
switch (success) { switch (success) {
case AUTH_SUCCESS: case AUTH_SUCCESS:
(void) sigaction(SIGTSTP, &osa, NULL);
rval = true; rval = true;
break; break;
case AUTH_INTR: case AUTH_INTR:
@@ -338,6 +371,7 @@ auth_getpass(const char *prompt, int timeout, int type)
{ {
struct sudo_conv_message msg; struct sudo_conv_message msg;
struct sudo_conv_reply repl; struct sudo_conv_reply repl;
sigset_t mask, omask;
debug_decl(auth_getpass, SUDO_DEBUG_AUTH) debug_decl(auth_getpass, SUDO_DEBUG_AUTH)
/* Mask user input if pwfeedback set and echo is off. */ /* Mask user input if pwfeedback set and echo is off. */
@@ -348,7 +382,14 @@ auth_getpass(const char *prompt, int timeout, int type)
if (def_visiblepw) if (def_visiblepw)
type |= SUDO_CONV_PROMPT_ECHO_OK; type |= SUDO_CONV_PROMPT_ECHO_OK;
/* Call conversation function */ /* Unblock SIGINT and SIGQUIT during password entry. */
/* XXX - do in tgetpass() itself instead? */
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
(void) sigprocmask(SIG_UNBLOCK, &mask, &omask);
/* Call conversation function. */
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
msg.msg_type = type; msg.msg_type = type;
msg.timeout = def_passwd_timeout * 60; msg.timeout = def_passwd_timeout * 60;
@@ -356,6 +397,10 @@ auth_getpass(const char *prompt, int timeout, int type)
memset(&repl, 0, sizeof(repl)); memset(&repl, 0, sizeof(repl));
sudo_conv(1, &msg, &repl); sudo_conv(1, &msg, &repl);
/* XXX - check for ENOTTY? */ /* XXX - check for ENOTTY? */
/* Restore previous signal mask. */
(void) sigprocmask(SIG_SETMASK, &omask, NULL);
debug_return_str_masked(repl.reply); debug_return_str_masked(repl.reply);
} }