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.
This commit is contained in:
Todd C. Miller
2015-09-07 06:06:08 -06:00
parent 00142c91fa
commit 0c70df5de9
4 changed files with 106 additions and 52 deletions

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

View File

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

View 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. */

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