Reimplement, hopefully with marginally fewer bugs. There is an
unfortunate amount of code duplication between the tty and non-tty paths, but the alternative is greatly increased complexity. git-svn-id: svn+ssh://svn.openpam.org/svn/openpam/trunk@687 185d5e19-27fe-0310-9dcf-9bff6b9f3609
This commit is contained in:
parent
3a53d5117b
commit
93d104bfd6
|
@ -40,9 +40,11 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <setjmp.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -56,108 +58,253 @@
|
||||||
|
|
||||||
int openpam_ttyconv_timeout = 0;
|
int openpam_ttyconv_timeout = 0;
|
||||||
|
|
||||||
|
volatile sig_atomic_t caught_signal;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle incoming signals during tty conversation
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
timeout(int sig)
|
catch_signal(int signo)
|
||||||
{
|
{
|
||||||
|
|
||||||
(void)sig;
|
switch (signo) {
|
||||||
|
case SIGINT:
|
||||||
|
case SIGQUIT:
|
||||||
|
case SIGTERM:
|
||||||
|
caught_signal = signo;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
/*
|
||||||
prompt(const char *msg)
|
* Accept a response from the user on a tty
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
prompt_tty(int ifd, int ofd, const char *message, char *response, int echo)
|
||||||
{
|
{
|
||||||
char buf[PAM_MAX_RESP_SIZE];
|
struct sigaction action;
|
||||||
struct sigaction action, saved_action;
|
struct sigaction saction_sigint, saction_sigquit, saction_sigterm;
|
||||||
sigset_t saved_sigset, the_sigset;
|
struct termios tcattr;
|
||||||
unsigned int saved_alarm;
|
struct timeval now, target, remaining;
|
||||||
int eof, error, fd;
|
int remaining_ms;
|
||||||
size_t len;
|
tcflag_t slflag;
|
||||||
char *retval;
|
struct pollfd pfd;
|
||||||
|
int serrno;
|
||||||
|
int pos, ret;
|
||||||
char ch;
|
char ch;
|
||||||
|
|
||||||
sigemptyset(&the_sigset);
|
/* write prompt */
|
||||||
sigaddset(&the_sigset, SIGINT);
|
if (write(ofd, message, strlen(message)) < 0) {
|
||||||
sigaddset(&the_sigset, SIGTSTP);
|
openpam_log(PAM_LOG_ERROR, "write(): %m");
|
||||||
sigprocmask(SIG_SETMASK, &the_sigset, &saved_sigset);
|
return (-1);
|
||||||
action.sa_handler = &timeout;
|
}
|
||||||
action.sa_flags = 0;
|
|
||||||
sigemptyset(&action.sa_mask);
|
/* turn echo off if requested */
|
||||||
sigaction(SIGALRM, &action, &saved_action);
|
slflag = 0; /* prevent bogus uninitialized variable warning */
|
||||||
fputs(msg, stdout);
|
if (!echo) {
|
||||||
fflush(stdout);
|
if (tcgetattr(ifd, &tcattr) != 0) {
|
||||||
#ifdef HAVE_FPURGE
|
openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m");
|
||||||
fpurge(stdin);
|
return (-1);
|
||||||
#endif
|
}
|
||||||
fd = fileno(stdin);
|
slflag = tcattr.c_lflag;
|
||||||
buf[0] = '\0';
|
tcattr.c_lflag &= ~ECHO;
|
||||||
eof = error = 0;
|
if (tcsetattr(ifd, TCSAFLUSH, &tcattr) != 0) {
|
||||||
saved_alarm = 0;
|
openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m");
|
||||||
if (openpam_ttyconv_timeout >= 0)
|
return (-1);
|
||||||
saved_alarm = alarm(openpam_ttyconv_timeout);
|
|
||||||
ch = '\0';
|
|
||||||
for (len = 0; ch != '\n' && !eof && !error; ++len) {
|
|
||||||
switch (read(fd, &ch, 1)) {
|
|
||||||
case 1:
|
|
||||||
if (len < PAM_MAX_RESP_SIZE - 1) {
|
|
||||||
buf[len + 1] = '\0';
|
|
||||||
buf[len] = ch;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
eof = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error = errno;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (openpam_ttyconv_timeout >= 0)
|
|
||||||
alarm(0);
|
/* install signal handlers */
|
||||||
sigaction(SIGALRM, &saved_action, NULL);
|
caught_signal = 0;
|
||||||
sigprocmask(SIG_SETMASK, &saved_sigset, NULL);
|
action.sa_handler = &catch_signal;
|
||||||
if (saved_alarm > 0)
|
action.sa_flags = 0;
|
||||||
alarm(saved_alarm);
|
sigfillset(&action.sa_mask);
|
||||||
if (error == EINTR)
|
sigaction(SIGINT, &action, &saction_sigint);
|
||||||
fputs(" timeout!", stderr);
|
sigaction(SIGQUIT, &action, &saction_sigquit);
|
||||||
if (error || eof) {
|
sigaction(SIGTERM, &action, &saction_sigterm);
|
||||||
fputs("\n", stderr);
|
|
||||||
memset(buf, 0, sizeof(buf));
|
/* compute timeout */
|
||||||
return (NULL);
|
if (openpam_ttyconv_timeout > 0) {
|
||||||
|
(void)gettimeofday(&now, NULL);
|
||||||
|
remaining.tv_sec = openpam_ttyconv_timeout;
|
||||||
|
remaining.tv_usec = 0;
|
||||||
|
timeradd(&now, &remaining, &target);
|
||||||
|
} else {
|
||||||
|
/* prevent bogus uninitialized variable warning */
|
||||||
|
now.tv_sec = now.tv_usec = 0;
|
||||||
|
remaining.tv_sec = remaining.tv_usec = 0;
|
||||||
|
target.tv_sec = target.tv_usec = 0;
|
||||||
}
|
}
|
||||||
/* trim trailing whitespace */
|
|
||||||
for (len = strlen(buf); len > 0; --len)
|
/* input loop */
|
||||||
if (buf[len - 1] != '\r' && buf[len - 1] != '\n')
|
pos = 0;
|
||||||
|
ret = -1;
|
||||||
|
serrno = 0;
|
||||||
|
while (!caught_signal) {
|
||||||
|
pfd.fd = ifd;
|
||||||
|
pfd.events = POLLIN;
|
||||||
|
pfd.revents = 0;
|
||||||
|
if (openpam_ttyconv_timeout > 0) {
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
if (timercmp(&now, &target, >))
|
||||||
|
break;
|
||||||
|
timersub(&target, &now, &remaining);
|
||||||
|
remaining_ms = remaining.tv_sec * 1000 +
|
||||||
|
remaining.tv_usec / 1000;
|
||||||
|
} else {
|
||||||
|
remaining_ms = INFTIM;
|
||||||
|
}
|
||||||
|
if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
|
||||||
|
serrno = errno;
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
openpam_log(PAM_LOG_ERROR, "poll(): %m");
|
||||||
break;
|
break;
|
||||||
buf[len] = '\0';
|
} else if (ret == 0) {
|
||||||
retval = strdup(buf);
|
/* timeout */
|
||||||
memset(buf, 0, sizeof(buf));
|
write(ofd, " timed out", 10);
|
||||||
return (retval);
|
openpam_log(PAM_LOG_NOTICE, "timed out");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((ret = read(ifd, &ch, 1)) < 0) {
|
||||||
|
serrno = errno;
|
||||||
|
openpam_log(PAM_LOG_ERROR, "read(): %m");
|
||||||
|
break;
|
||||||
|
} else if (ret == 0 || ch == '\n') {
|
||||||
|
response[pos] = '\0';
|
||||||
|
ret = pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (pos + 1 < PAM_MAX_RESP_SIZE)
|
||||||
|
response[pos++] = ch;
|
||||||
|
/* overflow is discarded */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* restore tty state */
|
||||||
|
if (!echo) {
|
||||||
|
tcattr.c_lflag = slflag;
|
||||||
|
if (tcsetattr(ifd, 0, &tcattr) != 0) {
|
||||||
|
/* treat as non-fatal, since we have our answer */
|
||||||
|
openpam_log(PAM_LOG_NOTICE, "tcsetattr(): %m");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* restore signal handlers and re-post caught signal*/
|
||||||
|
sigaction(SIGINT, &saction_sigint, NULL);
|
||||||
|
sigaction(SIGQUIT, &saction_sigquit, NULL);
|
||||||
|
sigaction(SIGTERM, &saction_sigterm, NULL);
|
||||||
|
if (caught_signal != 0) {
|
||||||
|
openpam_log(PAM_LOG_ERROR, "caught signal %d",
|
||||||
|
(int)caught_signal);
|
||||||
|
raise((int)caught_signal);
|
||||||
|
/* if raise() had no effect... */
|
||||||
|
serrno = EINTR;
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* done */
|
||||||
|
write(ofd, "\n", 1);
|
||||||
|
errno = serrno;
|
||||||
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
/*
|
||||||
prompt_echo_off(const char *msg)
|
* Accept a response from the user on a non-tty stdin.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
prompt_notty(const char *message, char *response)
|
||||||
{
|
{
|
||||||
struct termios tattr;
|
struct timeval now, target, remaining;
|
||||||
tcflag_t lflag;
|
int remaining_ms;
|
||||||
char *ret;
|
struct pollfd pfd;
|
||||||
int fd;
|
int ch, pos, ret;
|
||||||
|
|
||||||
fd = fileno(stdin);
|
/* show prompt */
|
||||||
if (tcgetattr(fd, &tattr) != 0) {
|
fputs(message, stdout);
|
||||||
openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m");
|
fflush(stdout);
|
||||||
return (NULL);
|
|
||||||
|
/* compute timeout */
|
||||||
|
if (openpam_ttyconv_timeout > 0) {
|
||||||
|
(void)gettimeofday(&now, NULL);
|
||||||
|
remaining.tv_sec = openpam_ttyconv_timeout;
|
||||||
|
remaining.tv_usec = 0;
|
||||||
|
timeradd(&now, &remaining, &target);
|
||||||
|
} else {
|
||||||
|
/* prevent bogus uninitialized variable warning */
|
||||||
|
now.tv_sec = now.tv_usec = 0;
|
||||||
|
remaining.tv_sec = remaining.tv_usec = 0;
|
||||||
|
target.tv_sec = target.tv_usec = 0;
|
||||||
}
|
}
|
||||||
lflag = tattr.c_lflag;
|
|
||||||
tattr.c_lflag &= ~ECHO;
|
/* input loop */
|
||||||
if (tcsetattr(fd, TCSAFLUSH, &tattr) != 0) {
|
pos = 0;
|
||||||
openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m");
|
for (;;) {
|
||||||
return (NULL);
|
pfd.fd = STDIN_FILENO;
|
||||||
|
pfd.events = POLLIN;
|
||||||
|
pfd.revents = 0;
|
||||||
|
if (openpam_ttyconv_timeout > 0) {
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
if (timercmp(&now, &target, >))
|
||||||
|
break;
|
||||||
|
timersub(&target, &now, &remaining);
|
||||||
|
remaining_ms = remaining.tv_sec * 1000 +
|
||||||
|
remaining.tv_usec / 1000;
|
||||||
|
} else {
|
||||||
|
remaining_ms = INFTIM;
|
||||||
|
}
|
||||||
|
if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
|
||||||
|
/* interrupt is ok, everything else -> bail */
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
perror("\nopenpam_ttyconv");
|
||||||
|
return (-1);
|
||||||
|
} else if (ret == 0) {
|
||||||
|
/* timeout */
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* input */
|
||||||
|
if ((ch = getchar()) == EOF && ferror(stdin)) {
|
||||||
|
perror("\nopenpam_ttyconv");
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if (ch == EOF || ch == '\n') {
|
||||||
|
response[pos] = '\0';
|
||||||
|
return (pos);
|
||||||
|
}
|
||||||
|
if (pos + 1 < PAM_MAX_RESP_SIZE)
|
||||||
|
response[pos++] = ch;
|
||||||
|
/* overflow is discarded */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ret = prompt(msg);
|
fputs("\nopenpam_ttyconv: timeout\n", stderr);
|
||||||
tattr.c_lflag = lflag;
|
return (-1);
|
||||||
(void)tcsetattr(fd, TCSANOW, &tattr);
|
}
|
||||||
if (ret != NULL)
|
|
||||||
fputs("\n", stdout);
|
/*
|
||||||
|
* Determine whether stdin is a tty; if not, try to open the tty; in
|
||||||
|
* either case, call the appropriate method.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
prompt(const char *message, char *response, int echo)
|
||||||
|
{
|
||||||
|
int ifd, ofd, ret;
|
||||||
|
|
||||||
|
if (isatty(STDIN_FILENO)) {
|
||||||
|
fflush(stdout);
|
||||||
|
#ifdef HAVE_FPURGE
|
||||||
|
fpurge(stdin);
|
||||||
|
#endif
|
||||||
|
ifd = STDIN_FILENO;
|
||||||
|
ofd = STDOUT_FILENO;
|
||||||
|
} else {
|
||||||
|
if ((ifd = open("/dev/tty", O_RDWR)) < 0)
|
||||||
|
/* no way to prevent echo */
|
||||||
|
return (prompt_notty(message, response));
|
||||||
|
ofd = ifd;
|
||||||
|
}
|
||||||
|
ret = prompt_tty(ifd, ofd, message, response, echo);
|
||||||
|
if (ifd != STDIN_FILENO)
|
||||||
|
close(ifd);
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +320,7 @@ openpam_ttyconv(int n,
|
||||||
struct pam_response **resp,
|
struct pam_response **resp,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
|
char respbuf[PAM_MAX_RESP_SIZE];
|
||||||
struct pam_response *aresp;
|
struct pam_response *aresp;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -187,13 +335,13 @@ openpam_ttyconv(int n,
|
||||||
aresp[i].resp = NULL;
|
aresp[i].resp = NULL;
|
||||||
switch (msg[i]->msg_style) {
|
switch (msg[i]->msg_style) {
|
||||||
case PAM_PROMPT_ECHO_OFF:
|
case PAM_PROMPT_ECHO_OFF:
|
||||||
aresp[i].resp = prompt_echo_off(msg[i]->msg);
|
if (prompt(msg[i]->msg, respbuf, 0) < 0 ||
|
||||||
if (aresp[i].resp == NULL)
|
(aresp[i].resp = strdup(respbuf)) == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
case PAM_PROMPT_ECHO_ON:
|
case PAM_PROMPT_ECHO_ON:
|
||||||
aresp[i].resp = prompt(msg[i]->msg);
|
if (prompt(msg[i]->msg, respbuf, 1) < 0 ||
|
||||||
if (aresp[i].resp == NULL)
|
(aresp[i].resp = strdup(respbuf)) == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
case PAM_ERROR_MSG:
|
case PAM_ERROR_MSG:
|
||||||
|
@ -213,6 +361,7 @@ openpam_ttyconv(int n,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*resp = aresp;
|
*resp = aresp;
|
||||||
|
memset(respbuf, 0, sizeof respbuf);
|
||||||
RETURNC(PAM_SUCCESS);
|
RETURNC(PAM_SUCCESS);
|
||||||
fail:
|
fail:
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
|
@ -224,6 +373,7 @@ fail:
|
||||||
memset(aresp, 0, n * sizeof *aresp);
|
memset(aresp, 0, n * sizeof *aresp);
|
||||||
FREE(aresp);
|
FREE(aresp);
|
||||||
*resp = NULL;
|
*resp = NULL;
|
||||||
|
memset(respbuf, 0, sizeof respbuf);
|
||||||
RETURNC(PAM_CONV_ERR);
|
RETURNC(PAM_CONV_ERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue