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:
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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. */
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user