]> granicus.if.org Git - sudo/commitdiff
Implement suspend/resume callbacks for the conversation function.
authorTodd C. Miller <Todd.Miller@courtesan.com>
Mon, 7 Sep 2015 12:06:08 +0000 (06:06 -0600)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Mon, 7 Sep 2015 12:06:08 +0000 (06:06 -0600)
If suspended, close the timestamp file (dropping all locks).  On
resume, lock the record before reading the password.

For this to work properly we need to be able to run th callback
when tsetattr() suspends us, not just when the user does.  To
accomplish this the term_* functions now return EINTR if SIGTTOU
would be generated.  The caller now has to restart the term_*
function (and send itself SIGTTOU) instead of it being done
automatically.

lib/util/term.c
plugins/sudoers/check.c
plugins/sudoers/sudoreplay.c
src/tgetpass.c

index 3c73116c9144f219ed31cd41cf17cdccacc197df..ac5c096ef8d411ea846e160452f6d08225f9e8bf 100644 (file)
@@ -129,7 +129,6 @@ sudo_term_noecho_v1(int fd)
 {
     debug_decl(sudo_term_noecho, SUDO_DEBUG_UTIL)
 
-again:
     if (!changed && tcgetattr(fd, &oterm) != 0)
        debug_return_bool(false);
     (void) memcpy(&term, &oterm, sizeof(term));
@@ -141,11 +140,6 @@ again:
        changed = 1;
        debug_return_bool(true);
     }
-    if (got_sigttou) {
-       /* We were in the background, so oterm is probably bogus. */
-       kill(getpid(), SIGTTOU);
-       goto again;
-    }
     debug_return_bool(false);
 }
 
@@ -159,7 +153,6 @@ sudo_term_raw_v1(int fd, int isig)
     struct termios term;
     debug_decl(sudo_term_raw, SUDO_DEBUG_UTIL)
 
-again:
     if (!changed && tcgetattr(fd, &oterm) != 0)
        debug_return_bool(false);
     (void) memcpy(&term, &oterm, sizeof(term));
@@ -175,11 +168,6 @@ again:
        changed = 1;
        debug_return_bool(true);
     }
-    if (got_sigttou) {
-       /* We were in the background, so oterm is probably bogus. */
-       kill(getpid(), SIGTTOU);
-       goto again;
-    }
     debug_return_bool(false);
 }
 
@@ -192,7 +180,6 @@ sudo_term_cbreak_v1(int fd)
 {
     debug_decl(sudo_term_cbreak, SUDO_DEBUG_UTIL)
 
-again:
     if (!changed && tcgetattr(fd, &oterm) != 0)
        debug_return_bool(false);
     (void) memcpy(&term, &oterm, sizeof(term));
@@ -212,11 +199,6 @@ again:
        changed = 1;
        debug_return_bool(true);
     }
-    if (got_sigttou) {
-       /* We were in the background, so oterm is probably bogus. */
-       kill(getpid(), SIGTTOU);
-       goto again;
-    }
     debug_return_bool(false);
 }
 
@@ -230,15 +212,9 @@ sudo_term_copy_v1(int src, int dst)
     struct termios tt;
     debug_decl(sudo_term_copy, SUDO_DEBUG_UTIL)
 
-again:
     if (tcgetattr(src, &tt) != 0)
        debug_return_bool(false);
     if (tcsetattr_nobg(dst, TCSASOFT|TCSAFLUSH, &tt) == 0)
        debug_return_bool(true);
-    if (got_sigttou) {
-       /* We were in the background, so tt is probably bogus. */
-       kill(getpid(), SIGTTOU);
-       goto again;
-    }
     debug_return_bool(false);
 }
index 94e320d4eced6a44de837fa88429661c29c7deca..a540d030b25e171b021227906e3da156e6960fc7 100644 (file)
 static bool display_lecture(int);
 static struct passwd *get_authpw(int);
 
+struct getpass_closure {
+    void *cookie;
+    struct passwd *auth_pw;
+};
+
+/*
+ * Called when getpass is suspended so we can drop the lock.
+ */
+static int
+getpass_suspend(int signo, void *vclosure)
+{
+    struct getpass_closure *closure = vclosure;
+
+    timestamp_close(closure->cookie);
+    closure->cookie = NULL;
+    return 0;
+}
+
+/*
+ * Called when getpass is resumed so we can reacquire the lock.
+ */
+static int
+getpass_resume(int signo, void *vclosure)
+{
+    struct getpass_closure *closure = vclosure;
+
+    closure->cookie = timestamp_open(user_name, user_sid);
+    if (closure->cookie == NULL)
+       return -1;
+    if (!timestamp_lock(closure->cookie, closure->auth_pw))
+       return -1;
+    return 0;
+}
+
 /*
  * Returns true if the user successfully authenticates, false if not
  * or -1 on fatal error.
@@ -53,10 +87,11 @@ static struct passwd *get_authpw(int);
 static int
 check_user_interactive(int validated, int mode, struct passwd *auth_pw)
 {
+    struct sudo_conv_callback callback;
+    struct getpass_closure closure;
     int status = TS_ERROR;
     int rval = -1;
     char *prompt;
-    void *cookie;
     bool lectured;
     debug_decl(check_user_interactive, SUDOERS_DEBUG_AUTH)
 
@@ -65,9 +100,10 @@ check_user_interactive(int validated, int mode, struct passwd *auth_pw)
        SET(validated, FLAG_CHECK_USER);
 
     /* Open timestamp file and check its status. */
-    cookie = timestamp_open(user_name, user_sid);
-    if (timestamp_lock(cookie, auth_pw))
-       status = timestamp_status(cookie, auth_pw);
+    closure.auth_pw = auth_pw;
+    closure.cookie = timestamp_open(user_name, user_sid);
+    if (timestamp_lock(closure.cookie, auth_pw))
+       status = timestamp_status(closure.cookie, auth_pw);
 
     switch (status) {
     case TS_FATAL:
@@ -99,7 +135,14 @@ check_user_interactive(int validated, int mode, struct passwd *auth_pw)
        if (prompt == NULL)
            goto done;
 
-       rval = verify_user(auth_pw, prompt, validated, NULL); /* XXX */
+       /* Construct callback for getpass function. */
+       memset(&callback, 0, sizeof(callback));
+       callback.version = SUDO_CONV_CALLBACK_VERSION;
+       callback.closure = &closure;
+       callback.on_suspend = getpass_suspend;
+       callback.on_resume = getpass_resume;
+
+       rval = verify_user(auth_pw, prompt, validated, &callback);
        if (rval == true && lectured)
            (void)set_lectured();       /* lecture error not fatal */
        free(prompt);
@@ -112,11 +155,11 @@ check_user_interactive(int validated, int mode, struct passwd *auth_pw)
      */
     if (rval == true && ISSET(validated, VALIDATE_SUCCESS) &&
        !ISSET(mode, MODE_IGNORE_TICKET) && status != TS_ERROR) {
-       (void)timestamp_update(cookie, auth_pw);
+       (void)timestamp_update(closure.cookie, auth_pw);
     }
 done:
-    if (cookie != NULL)
-       timestamp_close(cookie);
+    if (closure.cookie != NULL)
+       timestamp_close(closure.cookie);
     debug_return_int(rval);
 }
 
index 5808d2b47101c34215a443f23f89a407562b8a29..3db09c2991d64e0c761aa0b717eb6b8f6b7630d0 100644 (file)
@@ -366,8 +366,11 @@ replay_session(const double max_wait, const char *decimal)
     /* Set stdin to raw mode if it is a tty */
     interactive = isatty(STDIN_FILENO);
     if (interactive) {
-       if (!sudo_term_raw(STDIN_FILENO, 1))
-           sudo_fatal(U_("unable to set tty to raw mode"));
+       while (!sudo_term_raw(STDIN_FILENO, 1)) {
+           if (errno != EINTR)
+               sudo_fatal(U_("unable to set tty to raw mode"));
+           kill(getpid(), SIGTTOU);
+       }
     }
 
     /* Setup event base and input/output events. */
index 07be9ea98e8baf2a4fbcab1d1c7a0282576c4209..e3355015a3222dd6fb6b9062c44773e86dc7cccf 100644 (file)
@@ -51,6 +51,24 @@ static void tgetpass_handler(int);
 static char *getln(int, char *, size_t, int);
 static char *sudo_askpass(const char *, const char *);
 
+static int
+suspend(int signo, struct sudo_conv_callback *callback)
+{
+    int rval = 0;
+    debug_decl(suspend, SUDO_DEBUG_CONV)
+
+    if (callback != NULL && callback->on_suspend != NULL) {
+       if (callback->on_suspend(signo, callback->closure) == -1)
+           rval = -1;
+    }
+    kill(getpid(), signo);
+    if (callback != NULL && callback->on_resume != NULL) {
+       if (callback->on_resume(signo, callback->closure) == -1)
+           rval = -1;
+    }
+    debug_return_int(rval);
+}
+
 /*
  * Like getpass(3) but with timeout and echo flags.
  */
@@ -106,13 +124,23 @@ restart:
 
     /*
      * If we are using a tty but are not the foreground pgrp this will
-     * generate SIGTTOU, so do it *before* installing the signal handlers.
+     * return EINTR.  We send ourself SIGTTOU bracketed by callbacks.
      */
     if (!ISSET(flags, TGP_ECHO)) {
-       if (ISSET(flags, TGP_MASK))
-           neednl = sudo_term_cbreak(input);
-       else
-           neednl = sudo_term_noecho(input);
+       for (;;) {
+           if (ISSET(flags, TGP_MASK))
+               neednl = sudo_term_cbreak(input);
+           else
+               neednl = sudo_term_noecho(input);
+           if (neednl || errno != EINTR)
+               break;
+           /* Received SIGTTOU, suspend the process. */
+           if (suspend(SIGTTOU, callback) == -1) {
+               if (input != STDIN_FILENO)
+                   (void) close(input);
+               debug_return_ptr(NULL);
+           }
+       }
     }
 
     /*
@@ -154,8 +182,19 @@ restart:
 
 restore:
     /* Restore old tty settings and signals. */
-    if (!ISSET(flags, TGP_ECHO))
-       sudo_term_restore(input, 1);
+    if (!ISSET(flags, TGP_ECHO)) {
+       for (;;) {
+           /* Restore old tty settings if possible. */
+           if (sudo_term_restore(input, 1) || errno != EINTR)
+               break;
+           /* Received SIGTTOU, suspend the process. */
+           if (suspend(SIGTTOU, callback) == -1) {
+               if (input != STDIN_FILENO)
+                   (void) close(input);
+               debug_return_ptr(NULL);
+           }
+       }
+    }
     (void) sigaction(SIGALRM, &savealrm, NULL);
     (void) sigaction(SIGINT, &saveint, NULL);
     (void) sigaction(SIGHUP, &savehup, NULL);
@@ -178,18 +217,11 @@ restore:
                case SIGTSTP:
                case SIGTTIN:
                case SIGTTOU:
-                   if (callback != NULL && callback->on_suspend != NULL)
-                       callback->on_suspend(i, callback->closure);
+                   if (suspend(i, callback) == 0)
+                       need_restart = 1;
                    break;
-           }
-           kill(getpid(), i);
-           switch (i) {
-               case SIGTSTP:
-               case SIGTTIN:
-               case SIGTTOU:
-                   if (callback != NULL && callback->on_resume != NULL)
-                       callback->on_resume(i, callback->closure);
-                   need_restart = 1;
+               default:
+                   kill(getpid(), i);
                    break;
            }
        }