From 0c70df5de952f0ba591cc264738731c70e6e7363 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Mon, 7 Sep 2015 06:06:08 -0600 Subject: [PATCH] Implement suspend/resume callbacks for the conversation function. 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 | 24 ------------- plugins/sudoers/check.c | 59 ++++++++++++++++++++++++++----- plugins/sudoers/sudoreplay.c | 7 ++-- src/tgetpass.c | 68 ++++++++++++++++++++++++++---------- 4 files changed, 106 insertions(+), 52 deletions(-) diff --git a/lib/util/term.c b/lib/util/term.c index 3c73116c9..ac5c096ef 100644 --- a/lib/util/term.c +++ b/lib/util/term.c @@ -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); } diff --git a/plugins/sudoers/check.c b/plugins/sudoers/check.c index 94e320d4e..a540d030b 100644 --- a/plugins/sudoers/check.c +++ b/plugins/sudoers/check.c @@ -46,6 +46,40 @@ 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); } diff --git a/plugins/sudoers/sudoreplay.c b/plugins/sudoers/sudoreplay.c index 5808d2b47..3db09c299 100644 --- a/plugins/sudoers/sudoreplay.c +++ b/plugins/sudoers/sudoreplay.c @@ -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. */ diff --git a/src/tgetpass.c b/src/tgetpass.c index 07be9ea98..e3355015a 100644 --- a/src/tgetpass.c +++ b/src/tgetpass.c @@ -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; } } -- 2.40.0