]> granicus.if.org Git - sudo/commitdiff
Block SIGINT and SIGQUIT while verifying passwords so that
authorTodd C. Miller <Todd.Miller@courtesan.com>
Sat, 27 Sep 2014 16:16:31 +0000 (10:16 -0600)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Sat, 27 Sep 2014 16:16:31 +0000 (10:16 -0600)
authentication modules that use sleep() are not interrupted.
If the user interrupted authentication, exit the loop.

plugins/sudoers/auth/sia.c
plugins/sudoers/auth/sudo_auth.c

index 9cea620b0d3960d468d58e9e0976bd027108099b..7df9c09ae9a72296c3d0e4849ca5159799a6cf4d 100644 (file)
@@ -43,6 +43,7 @@
 # include <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #include <pwd.h>
+#include <signal.h>
 #include <siad.h>
 
 #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
index 3e8062352e5ef47817f57dfd4cdbeb819e726c36..21e5c7d0bd19f97dfd36446e0f8ce92ca6d3c5c5 100644 (file)
@@ -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);
 }