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; } }