From: Todd C. Miller Date: Sat, 27 Sep 2014 16:16:31 +0000 (-0600) Subject: Block SIGINT and SIGQUIT while verifying passwords so that X-Git-Tag: SUDO_1_8_12^2~179 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=355834c76af75fe0cea59a6465f99919fdaff8f7;p=sudo 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. --- diff --git a/plugins/sudoers/auth/sia.c b/plugins/sudoers/auth/sia.c index 9cea620b0..7df9c09ae 100644 --- a/plugins/sudoers/auth/sia.c +++ b/plugins/sudoers/auth/sia.c @@ -43,6 +43,7 @@ # include #endif /* HAVE_UNISTD_H */ #include +#include #include #include "sudoers.h" @@ -62,6 +63,8 @@ static int sudo_collect(int timeout, int rendition, uchar_t *title, int nprompts, prompt_t *prompts) { + int rval; + sigset_t mask, omask; debug_decl(sudo_collect, SUDO_DEBUG_AUTH) switch (rendition) { @@ -82,7 +85,18 @@ sudo_collect(int timeout, int rendition, uchar_t *title, int nprompts, 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 @@ -118,7 +132,7 @@ sudo_sia_verify(struct passwd *pw, char *prompt, sudo_auth *auth) 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) debug_return_int(AUTH_SUCCESS); else diff --git a/plugins/sudoers/auth/sudo_auth.c b/plugins/sudoers/auth/sudo_auth.c index 3e8062352..21e5c7d0b 100644 --- a/plugins/sudoers/auth/sudo_auth.c +++ b/plugins/sudoers/auth/sudo_auth.c @@ -174,6 +174,15 @@ pass_warn(void) debug_return; } +static bool +user_interrupted(void) +{ + sigset_t mask; + + return (sigpending(&mask) == 0 && + (sigismember(&mask, SIGINT) || sigismember(&mask, SIGQUIT))); +} + /* * Verify the specified user. * 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; char *p; sudo_auth *auth; - sigaction_t sa, osa; + sigset_t mask, omask; + sigaction_t sa, saved_sigtstp; 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. */ if (auth_switch[0].name == NULL) { 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); } + /* 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) { 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 */ for (auth = auth_switch; auth->name; auth++) { if (IS_DISABLED(auth)) @@ -217,7 +245,7 @@ verify_user(struct passwd *pw, char *prompt, int validated) status = (auth->setup)(pw, &prompt, auth); if (status == AUTH_FAILURE) SET(auth->flags, FLAG_DISABLED); - else if (status == AUTH_FATAL) + else if (status == AUTH_FATAL || user_interrupted()) goto done; /* assume error msg already printed */ } } @@ -244,18 +272,23 @@ verify_user(struct passwd *pw, char *prompt, int validated) continue; success = auth->status = (auth->verify)(pw, p, auth); - if (auth->status != AUTH_FAILURE) - goto done; + if (success != AUTH_FAILURE) + break; } if (!standalone) memset_s(p, SUDO_CONV_REPL_MAX, 0, strlen(p)); - pass_warn(); + + if (success != AUTH_FAILURE) + goto done; } done: + /* Restore signal handlers and signal mask. */ + (void) sigaction(SIGTSTP, &saved_sigtstp, NULL); + (void) sigprocmask(SIG_SETMASK, &omask, NULL); + switch (success) { case AUTH_SUCCESS: - (void) sigaction(SIGTSTP, &osa, NULL); rval = true; break; case AUTH_INTR: @@ -338,6 +371,7 @@ auth_getpass(const char *prompt, int timeout, int type) { struct sudo_conv_message msg; struct sudo_conv_reply repl; + sigset_t mask, omask; debug_decl(auth_getpass, SUDO_DEBUG_AUTH) /* 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) 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)); msg.msg_type = type; msg.timeout = def_passwd_timeout * 60; @@ -356,6 +397,10 @@ auth_getpass(const char *prompt, int timeout, int type) memset(&repl, 0, sizeof(repl)); sudo_conv(1, &msg, &repl); /* XXX - check for ENOTTY? */ + + /* Restore previous signal mask. */ + (void) sigprocmask(SIG_SETMASK, &omask, NULL); + debug_return_str_masked(repl.reply); }