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) debug_decl(sudo_term_noecho, SUDO_DEBUG_UTIL)
again:
if (!changed && tcgetattr(fd, &oterm) != 0) if (!changed && tcgetattr(fd, &oterm) != 0)
debug_return_bool(false); debug_return_bool(false);
(void) memcpy(&term, &oterm, sizeof(term)); (void) memcpy(&term, &oterm, sizeof(term));
@@ -141,11 +140,6 @@ again:
changed = 1; changed = 1;
debug_return_bool(true); 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); debug_return_bool(false);
} }
@@ -159,7 +153,6 @@ sudo_term_raw_v1(int fd, int isig)
struct termios term; struct termios term;
debug_decl(sudo_term_raw, SUDO_DEBUG_UTIL) debug_decl(sudo_term_raw, SUDO_DEBUG_UTIL)
again:
if (!changed && tcgetattr(fd, &oterm) != 0) if (!changed && tcgetattr(fd, &oterm) != 0)
debug_return_bool(false); debug_return_bool(false);
(void) memcpy(&term, &oterm, sizeof(term)); (void) memcpy(&term, &oterm, sizeof(term));
@@ -175,11 +168,6 @@ again:
changed = 1; changed = 1;
debug_return_bool(true); 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); debug_return_bool(false);
} }
@@ -192,7 +180,6 @@ sudo_term_cbreak_v1(int fd)
{ {
debug_decl(sudo_term_cbreak, SUDO_DEBUG_UTIL) debug_decl(sudo_term_cbreak, SUDO_DEBUG_UTIL)
again:
if (!changed && tcgetattr(fd, &oterm) != 0) if (!changed && tcgetattr(fd, &oterm) != 0)
debug_return_bool(false); debug_return_bool(false);
(void) memcpy(&term, &oterm, sizeof(term)); (void) memcpy(&term, &oterm, sizeof(term));
@@ -212,11 +199,6 @@ again:
changed = 1; changed = 1;
debug_return_bool(true); 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); debug_return_bool(false);
} }
@@ -230,15 +212,9 @@ sudo_term_copy_v1(int src, int dst)
struct termios tt; struct termios tt;
debug_decl(sudo_term_copy, SUDO_DEBUG_UTIL) debug_decl(sudo_term_copy, SUDO_DEBUG_UTIL)
again:
if (tcgetattr(src, &tt) != 0) if (tcgetattr(src, &tt) != 0)
debug_return_bool(false); debug_return_bool(false);
if (tcsetattr_nobg(dst, TCSASOFT|TCSAFLUSH, &tt) == 0) if (tcsetattr_nobg(dst, TCSASOFT|TCSAFLUSH, &tt) == 0)
debug_return_bool(true); 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); debug_return_bool(false);
} }

View File

@@ -46,6 +46,40 @@
static bool display_lecture(int); static bool display_lecture(int);
static struct passwd *get_authpw(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 * Returns true if the user successfully authenticates, false if not
* or -1 on fatal error. * or -1 on fatal error.
@@ -53,10 +87,11 @@ static struct passwd *get_authpw(int);
static int static int
check_user_interactive(int validated, int mode, struct passwd *auth_pw) 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 status = TS_ERROR;
int rval = -1; int rval = -1;
char *prompt; char *prompt;
void *cookie;
bool lectured; bool lectured;
debug_decl(check_user_interactive, SUDOERS_DEBUG_AUTH) 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); SET(validated, FLAG_CHECK_USER);
/* Open timestamp file and check its status. */ /* Open timestamp file and check its status. */
cookie = timestamp_open(user_name, user_sid); closure.auth_pw = auth_pw;
if (timestamp_lock(cookie, auth_pw)) closure.cookie = timestamp_open(user_name, user_sid);
status = timestamp_status(cookie, auth_pw); if (timestamp_lock(closure.cookie, auth_pw))
status = timestamp_status(closure.cookie, auth_pw);
switch (status) { switch (status) {
case TS_FATAL: case TS_FATAL:
@@ -99,7 +135,14 @@ check_user_interactive(int validated, int mode, struct passwd *auth_pw)
if (prompt == NULL) if (prompt == NULL)
goto done; 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) if (rval == true && lectured)
(void)set_lectured(); /* lecture error not fatal */ (void)set_lectured(); /* lecture error not fatal */
free(prompt); free(prompt);
@@ -112,11 +155,11 @@ check_user_interactive(int validated, int mode, struct passwd *auth_pw)
*/ */
if (rval == true && ISSET(validated, VALIDATE_SUCCESS) && if (rval == true && ISSET(validated, VALIDATE_SUCCESS) &&
!ISSET(mode, MODE_IGNORE_TICKET) && status != TS_ERROR) { !ISSET(mode, MODE_IGNORE_TICKET) && status != TS_ERROR) {
(void)timestamp_update(cookie, auth_pw); (void)timestamp_update(closure.cookie, auth_pw);
} }
done: done:
if (cookie != NULL) if (closure.cookie != NULL)
timestamp_close(cookie); timestamp_close(closure.cookie);
debug_return_int(rval); 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 */ /* Set stdin to raw mode if it is a tty */
interactive = isatty(STDIN_FILENO); interactive = isatty(STDIN_FILENO);
if (interactive) { if (interactive) {
if (!sudo_term_raw(STDIN_FILENO, 1)) while (!sudo_term_raw(STDIN_FILENO, 1)) {
sudo_fatal(U_("unable to set tty to raw mode")); if (errno != EINTR)
sudo_fatal(U_("unable to set tty to raw mode"));
kill(getpid(), SIGTTOU);
}
} }
/* Setup event base and input/output events. */ /* 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 *getln(int, char *, size_t, int);
static char *sudo_askpass(const char *, const char *); 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. * 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 * 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_ECHO)) {
if (ISSET(flags, TGP_MASK)) for (;;) {
neednl = sudo_term_cbreak(input); if (ISSET(flags, TGP_MASK))
else neednl = sudo_term_cbreak(input);
neednl = sudo_term_noecho(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:
/* Restore old tty settings and signals. */ /* Restore old tty settings and signals. */
if (!ISSET(flags, TGP_ECHO)) if (!ISSET(flags, TGP_ECHO)) {
sudo_term_restore(input, 1); 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(SIGALRM, &savealrm, NULL);
(void) sigaction(SIGINT, &saveint, NULL); (void) sigaction(SIGINT, &saveint, NULL);
(void) sigaction(SIGHUP, &savehup, NULL); (void) sigaction(SIGHUP, &savehup, NULL);
@@ -178,18 +217,11 @@ restore:
case SIGTSTP: case SIGTSTP:
case SIGTTIN: case SIGTTIN:
case SIGTTOU: case SIGTTOU:
if (callback != NULL && callback->on_suspend != NULL) if (suspend(i, callback) == 0)
callback->on_suspend(i, callback->closure); need_restart = 1;
break; break;
} default:
kill(getpid(), i); 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;
break; break;
} }
} }