Changeset 687 in openpam


Ignore:
Timestamp:
Jul 11, 2013, 4:37:25 PM (7 years ago)
Author:
Dag-Erling Smørgrav
Message:

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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/lib/libpam/openpam_ttyconv.c

    r667 r687  
    4141
    4242#include <sys/types.h>
     43#include <sys/poll.h>
     44#include <sys/time.h>
    4345
    4446#include <errno.h>
    45 #include <setjmp.h>
     47#include <fcntl.h>
    4648#include <signal.h>
    4749#include <stdio.h>
     
    5759int openpam_ttyconv_timeout = 0;
    5860
     61volatile sig_atomic_t caught_signal;
     62
     63/*
     64 * Handle incoming signals during tty conversation
     65 */
    5966static void
    60 timeout(int sig)
     67catch_signal(int signo)
    6168{
    6269
    63         (void)sig;
     70        switch (signo) {
     71        case SIGINT:
     72        case SIGQUIT:
     73        case SIGTERM:
     74                caught_signal = signo;
     75                break;
     76        }
    6477}
    6578
    66 static char *
    67 prompt(const char *msg)
     79/*
     80 * Accept a response from the user on a tty
     81 */
     82static int
     83prompt_tty(int ifd, int ofd, const char *message, char *response, int echo)
    6884{
    69         char buf[PAM_MAX_RESP_SIZE];
    70         struct sigaction action, saved_action;
    71         sigset_t saved_sigset, the_sigset;
    72         unsigned int saved_alarm;
    73         int eof, error, fd;
    74         size_t len;
    75         char *retval;
     85        struct sigaction action;
     86        struct sigaction saction_sigint, saction_sigquit, saction_sigterm;
     87        struct termios tcattr;
     88        struct timeval now, target, remaining;
     89        int remaining_ms;
     90        tcflag_t slflag;
     91        struct pollfd pfd;
     92        int serrno;
     93        int pos, ret;
    7694        char ch;
    7795
    78         sigemptyset(&the_sigset);
    79         sigaddset(&the_sigset, SIGINT);
    80         sigaddset(&the_sigset, SIGTSTP);
    81         sigprocmask(SIG_SETMASK, &the_sigset, &saved_sigset);
    82         action.sa_handler = &timeout;
     96        /* write prompt */
     97        if (write(ofd, message, strlen(message)) < 0) {
     98                openpam_log(PAM_LOG_ERROR, "write(): %m");
     99                return (-1);
     100        }
     101
     102        /* turn echo off if requested */
     103        slflag = 0; /* prevent bogus uninitialized variable warning */
     104        if (!echo) {
     105                if (tcgetattr(ifd, &tcattr) != 0) {
     106                        openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m");
     107                        return (-1);
     108                }
     109                slflag = tcattr.c_lflag;
     110                tcattr.c_lflag &= ~ECHO;
     111                if (tcsetattr(ifd, TCSAFLUSH, &tcattr) != 0) {
     112                        openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m");
     113                        return (-1);
     114                }
     115        }
     116
     117        /* install signal handlers */
     118        caught_signal = 0;
     119        action.sa_handler = &catch_signal;
    83120        action.sa_flags = 0;
    84         sigemptyset(&action.sa_mask);
    85         sigaction(SIGALRM, &action, &saved_action);
    86         fputs(msg, stdout);
     121        sigfillset(&action.sa_mask);
     122        sigaction(SIGINT, &action, &saction_sigint);
     123        sigaction(SIGQUIT, &action, &saction_sigquit);
     124        sigaction(SIGTERM, &action, &saction_sigterm);
     125
     126        /* compute timeout */
     127        if (openpam_ttyconv_timeout > 0) {
     128                (void)gettimeofday(&now, NULL);
     129                remaining.tv_sec = openpam_ttyconv_timeout;
     130                remaining.tv_usec = 0;
     131                timeradd(&now, &remaining, &target);
     132        } else {
     133                /* prevent bogus uninitialized variable warning */
     134                now.tv_sec = now.tv_usec = 0;
     135                remaining.tv_sec = remaining.tv_usec = 0;
     136                target.tv_sec = target.tv_usec = 0;
     137        }
     138
     139        /* input loop */
     140        pos = 0;
     141        ret = -1;
     142        serrno = 0;
     143        while (!caught_signal) {
     144                pfd.fd = ifd;
     145                pfd.events = POLLIN;
     146                pfd.revents = 0;
     147                if (openpam_ttyconv_timeout > 0) {
     148                        gettimeofday(&now, NULL);
     149                        if (timercmp(&now, &target, >))
     150                                break;
     151                        timersub(&target, &now, &remaining);
     152                        remaining_ms = remaining.tv_sec * 1000 +
     153                            remaining.tv_usec / 1000;
     154                } else {
     155                        remaining_ms = INFTIM;
     156                }
     157                if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
     158                        serrno = errno;
     159                        if (errno == EINTR)
     160                                continue;
     161                        openpam_log(PAM_LOG_ERROR, "poll(): %m");
     162                        break;
     163                } else if (ret == 0) {
     164                        /* timeout */
     165                        write(ofd, " timed out", 10);
     166                        openpam_log(PAM_LOG_NOTICE, "timed out");
     167                        break;
     168                }
     169                if ((ret = read(ifd, &ch, 1)) < 0) {
     170                        serrno = errno;
     171                        openpam_log(PAM_LOG_ERROR, "read(): %m");
     172                        break;
     173                } else if (ret == 0 || ch == '\n') {
     174                        response[pos] = '\0';
     175                        ret = pos;
     176                        break;
     177                }
     178                if (pos + 1 < PAM_MAX_RESP_SIZE)
     179                        response[pos++] = ch;
     180                /* overflow is discarded */
     181        }
     182
     183        /* restore tty state */
     184        if (!echo) {
     185                tcattr.c_lflag = slflag;
     186                if (tcsetattr(ifd, 0, &tcattr) != 0) {
     187                        /* treat as non-fatal, since we have our answer */
     188                        openpam_log(PAM_LOG_NOTICE, "tcsetattr(): %m");
     189                }
     190        }
     191
     192        /* restore signal handlers and re-post caught signal*/
     193        sigaction(SIGINT, &saction_sigint, NULL);
     194        sigaction(SIGQUIT, &saction_sigquit, NULL);
     195        sigaction(SIGTERM, &saction_sigterm, NULL);
     196        if (caught_signal != 0) {
     197                openpam_log(PAM_LOG_ERROR, "caught signal %d",
     198                    (int)caught_signal);
     199                raise((int)caught_signal);
     200                /* if raise() had no effect... */
     201                serrno = EINTR;
     202                ret = -1;
     203        }
     204
     205        /* done */
     206        write(ofd, "\n", 1);
     207        errno = serrno;
     208        return (ret);
     209}
     210
     211/*
     212 * Accept a response from the user on a non-tty stdin.
     213 */
     214static int
     215prompt_notty(const char *message, char *response)
     216{
     217        struct timeval now, target, remaining;
     218        int remaining_ms;
     219        struct pollfd pfd;
     220        int ch, pos, ret;
     221
     222        /* show prompt */
     223        fputs(message, stdout);
    87224        fflush(stdout);
     225
     226        /* compute timeout */
     227        if (openpam_ttyconv_timeout > 0) {
     228                (void)gettimeofday(&now, NULL);
     229                remaining.tv_sec = openpam_ttyconv_timeout;
     230                remaining.tv_usec = 0;
     231                timeradd(&now, &remaining, &target);
     232        } else {
     233                /* prevent bogus uninitialized variable warning */
     234                now.tv_sec = now.tv_usec = 0;
     235                remaining.tv_sec = remaining.tv_usec = 0;
     236                target.tv_sec = target.tv_usec = 0;
     237        }
     238
     239        /* input loop */
     240        pos = 0;
     241        for (;;) {
     242                pfd.fd = STDIN_FILENO;
     243                pfd.events = POLLIN;
     244                pfd.revents = 0;
     245                if (openpam_ttyconv_timeout > 0) {
     246                        gettimeofday(&now, NULL);
     247                        if (timercmp(&now, &target, >))
     248                                break;
     249                        timersub(&target, &now, &remaining);
     250                        remaining_ms = remaining.tv_sec * 1000 +
     251                            remaining.tv_usec / 1000;
     252                } else {
     253                        remaining_ms = INFTIM;
     254                }
     255                if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
     256                        /* interrupt is ok, everything else -> bail */
     257                        if (errno == EINTR)
     258                                continue;
     259                        perror("\nopenpam_ttyconv");
     260                        return (-1);
     261                } else if (ret == 0) {
     262                        /* timeout */
     263                        break;
     264                } else {
     265                        /* input */
     266                        if ((ch = getchar()) == EOF && ferror(stdin)) {
     267                                perror("\nopenpam_ttyconv");
     268                                return (-1);
     269                        }
     270                        if (ch == EOF || ch == '\n') {
     271                                response[pos] = '\0';
     272                                return (pos);
     273                        }
     274                        if (pos + 1 < PAM_MAX_RESP_SIZE)
     275                                response[pos++] = ch;
     276                        /* overflow is discarded */
     277                }
     278        }
     279        fputs("\nopenpam_ttyconv: timeout\n", stderr);
     280        return (-1);
     281}
     282
     283/*
     284 * Determine whether stdin is a tty; if not, try to open the tty; in
     285 * either case, call the appropriate method.
     286 */
     287static int
     288prompt(const char *message, char *response, int echo)
     289{
     290        int ifd, ofd, ret;
     291
     292        if (isatty(STDIN_FILENO)) {
     293                fflush(stdout);
    88294#ifdef HAVE_FPURGE
    89         fpurge(stdin);
     295                fpurge(stdin);
    90296#endif
    91         fd = fileno(stdin);
    92         buf[0] = '\0';
    93         eof = error = 0;
    94         saved_alarm = 0;
    95         if (openpam_ttyconv_timeout >= 0)
    96                 saved_alarm = alarm(openpam_ttyconv_timeout);
    97         ch = '\0';
    98         for (len = 0; ch != '\n' && !eof && !error; ++len) {
    99                 switch (read(fd, &ch, 1)) {
    100                 case 1:
    101                         if (len < PAM_MAX_RESP_SIZE - 1) {
    102                                 buf[len + 1] = '\0';
    103                                 buf[len] = ch;
    104                         }
    105                         break;
    106                 case 0:
    107                         eof = 1;
    108                         break;
    109                 default:
    110                         error = errno;
    111                         break;
    112                 }
    113         }
    114         if (openpam_ttyconv_timeout >= 0)
    115                 alarm(0);
    116         sigaction(SIGALRM, &saved_action, NULL);
    117         sigprocmask(SIG_SETMASK, &saved_sigset, NULL);
    118         if (saved_alarm > 0)
    119                 alarm(saved_alarm);
    120         if (error == EINTR)
    121                 fputs(" timeout!", stderr);
    122         if (error || eof) {
    123                 fputs("\n", stderr);
    124                 memset(buf, 0, sizeof(buf));
    125                 return (NULL);
    126         }
    127         /* trim trailing whitespace */
    128         for (len = strlen(buf); len > 0; --len)
    129                 if (buf[len - 1] != '\r' && buf[len - 1] != '\n')
    130                         break;
    131         buf[len] = '\0';
    132         retval = strdup(buf);
    133         memset(buf, 0, sizeof(buf));
    134         return (retval);
    135 }
    136 
    137 static char *
    138 prompt_echo_off(const char *msg)
    139 {
    140         struct termios tattr;
    141         tcflag_t lflag;
    142         char *ret;
    143         int fd;
    144 
    145         fd = fileno(stdin);
    146         if (tcgetattr(fd, &tattr) != 0) {
    147                 openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m");
    148                 return (NULL);
    149         }
    150         lflag = tattr.c_lflag;
    151         tattr.c_lflag &= ~ECHO;
    152         if (tcsetattr(fd, TCSAFLUSH, &tattr) != 0) {
    153                 openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m");
    154                 return (NULL);
    155         }
    156         ret = prompt(msg);
    157         tattr.c_lflag = lflag;
    158         (void)tcsetattr(fd, TCSANOW, &tattr);
    159         if (ret != NULL)
    160                 fputs("\n", stdout);
     297                ifd = STDIN_FILENO;
     298                ofd = STDOUT_FILENO;
     299        } else {
     300                if ((ifd = open("/dev/tty", O_RDWR)) < 0)
     301                        /* no way to prevent echo */
     302                        return (prompt_notty(message, response));
     303                ofd = ifd;
     304        }
     305        ret = prompt_tty(ifd, ofd, message, response, echo);
     306        if (ifd != STDIN_FILENO)
     307                close(ifd);
    161308        return (ret);
    162309}
     
    174321         void *data)
    175322{
     323        char respbuf[PAM_MAX_RESP_SIZE];
    176324        struct pam_response *aresp;
    177325        int i;
     
    188336                switch (msg[i]->msg_style) {
    189337                case PAM_PROMPT_ECHO_OFF:
    190                         aresp[i].resp = prompt_echo_off(msg[i]->msg);
    191                         if (aresp[i].resp == NULL)
     338                        if (prompt(msg[i]->msg, respbuf, 0) < 0 ||
     339                            (aresp[i].resp = strdup(respbuf)) == NULL)
    192340                                goto fail;
    193341                        break;
    194342                case PAM_PROMPT_ECHO_ON:
    195                         aresp[i].resp = prompt(msg[i]->msg);
    196                         if (aresp[i].resp == NULL)
     343                        if (prompt(msg[i]->msg, respbuf, 1) < 0 ||
     344                            (aresp[i].resp = strdup(respbuf)) == NULL)
    197345                                goto fail;
    198346                        break;
     
    214362        }
    215363        *resp = aresp;
     364        memset(respbuf, 0, sizeof respbuf);
    216365        RETURNC(PAM_SUCCESS);
    217366fail:
     
    225374        FREE(aresp);
    226375        *resp = NULL;
     376        memset(respbuf, 0, sizeof respbuf);
    227377        RETURNC(PAM_CONV_ERR);
    228378}
Note: See TracChangeset for help on using the changeset viewer.