]> granicus.if.org Git - sudo/commitdiff
Add support for interrupting/suspending tgetpass via keyboard input.
authorTodd C. Miller <Todd.Miller@courtesan.com>
Sun, 9 Dec 2001 05:14:23 +0000 (05:14 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Sun, 9 Dec 2001 05:14:23 +0000 (05:14 +0000)
If you suspend sudo from the password prompt and resume it will
re-prompt you.

tgetpass.c

index 184688c20ddcf18909cfcce35456cc70b9e0e883..8d78f1c8038c034986e0ab8e6ce5da8c0a91a67a 100644 (file)
@@ -78,8 +78,19 @@ static const char rcsid[] = "$Sudo$";
 #endif /* lint */
 
 #ifndef TCSASOFT
-#define TCSASOFT       0
-#endif /* TCSASOFT */
+# define TCSASOFT      0
+#endif
+#ifndef ECHONL
+# define ECHONL        0
+#endif
+
+#ifndef _POSIX_VDISABLE
+# ifdef VDISABLE
+#  define _POSIX_VDISABLE      VDISABLE
+# else
+#  define _POSIX_VDISABLE      0
+# endif
+#endif
 
 /*
  * Abstract method of getting at the term flags.
@@ -105,7 +116,10 @@ static const char rcsid[] = "$Sudo$";
 # endif /* HAVE_TERMIO_H */
 #endif /* HAVE_TERMIOS_H */
 
+static int signo;              /* XXX - should be volatile sig_atomic_t */
+
 static char *tgetline __P((int, char *, size_t, int));
+static void handler __P((int));
 
 /*
  * Like getpass(3) but with timeout and echo flags.
@@ -116,10 +130,13 @@ tgetpass(prompt, timeout, flags)
     int timeout;
     int flags;
 {
-    struct TERM term, oterm;
-    int input, output;
+    struct sigaction sa, saveint, savehup, savequit, saveterm, savetstp;
     static char buf[SUDO_PASS_MAX + 1];
+    int input, output, save_errno;
+    struct TERM term, oterm;
+    char *pass;
 
+restart:
     /* Open /dev/tty for reading/writing if possible else use stdin/stderr. */
     if ((flags & TGP_STDIN) ||
        (input = output = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1) {
@@ -130,28 +147,65 @@ tgetpass(prompt, timeout, flags)
     if (prompt)
        (void) write(output, prompt, strlen(prompt));
 
-    /* Turn echo off/on as specified by flags.  */
-    (void) term_getattr(input, &oterm);
-    (void) memcpy(&term, &oterm, sizeof(term));
-    if ((flags & TGP_ECHO) && !(term.tflags & ECHO))
-       term.tflags |= ECHO;
-    else if (!(flags & TGP_ECHO) && (term.tflags & ECHO))
-       term.tflags &= ~ECHO;
-    (void) term_setattr(input, &term);
+    /*
+     * Catch signals that would otherwise cause the user to end
+     * up with echo turned off in the shell.  Don't worry about
+     * things like SIGALRM and SIGPIPE for now.
+     */
+    sigemptyset(&sa.sa_mask);
+    sa.sa_flags = 0;           /* don't restart system calls */
+    sa.sa_handler = handler;
+    (void)sigaction(SIGINT, &sa, &saveint);
+    (void)sigaction(SIGHUP, &sa, &savehup);
+    (void)sigaction(SIGQUIT, &sa, &savequit);
+    (void)sigaction(SIGTERM, &sa, &saveterm);
+    (void)sigaction(SIGTSTP, &sa, &savetstp);
 
-    buf[0] = '\0';
-    tgetline(input, buf, sizeof(buf), timeout);
+    /* Turn echo off/on as specified by flags.  */
+    if (term_getattr(input, &oterm) == 0) {
+       (void) memcpy(&term, &oterm, sizeof(term));
+       if (!(flags & TGP_ECHO))
+           term.tflags &= ~(ECHO | ECHONL);
+#ifdef VSTATUS
+       term.c_cc[VSTATUS] = _POSIX_VDISABLE;
+#endif
+       (void) term_setattr(input, &term);
+    } else {
+       memset(&term, 0, sizeof(term));
+       memset(&oterm, 0, sizeof(oterm));
+    }
 
-    /* Restore old tty flags.  */
-    (void) term_setattr(input, &oterm);
+    pass = tgetline(input, buf, sizeof(buf), timeout);
+    save_errno = errno;
 
-    if (!(flags & TGP_ECHO))
+    if (!(term.tflags & ECHO))
        (void) write(output, "\n", 1);
 
+    /* Restore old tty settings and signals. */
+    if (memcmp(&term, &oterm, sizeof(term)) != 0)
+       (void) term_setattr(input, &oterm);
+    (void) sigaction(SIGINT, &saveint, NULL);
+    (void) sigaction(SIGHUP, &savehup, NULL);
+    (void) sigaction(SIGQUIT, &savequit, NULL);
+    (void) sigaction(SIGTERM, &saveterm, NULL);
+    (void) sigaction(SIGTSTP, &savetstp, NULL);
     if (input != STDIN_FILENO)
        (void) close(input);
 
-    return(buf);
+    /*
+     * If we were interrupted by a signal, resend it to ourselves
+     * now that we have restored the signal handlers.
+     */
+    if (signo) {
+       kill(getpid(), signo); 
+       if (signo == SIGTSTP) {
+           signo = 0;
+           goto restart;
+       }
+    }
+
+    errno = save_errno;
+    return(pass);
 }
 
 /*
@@ -164,15 +218,17 @@ tgetline(fd, buf, bufsiz, timeout)
     size_t bufsiz;
     int timeout;
 {
-    size_t left;
-    int n;
     fd_set *readfds = NULL;
     struct timeval tv;
-    char c;
+    size_t left;
     char *cp;
+    char c;
+    int n;
 
-    if (bufsiz == 0)
+    if (bufsiz == 0) {
+       errno = EINVAL;
        return(NULL);                   /* sanity */
+    }
 
     cp = buf;
     left = bufsiz;
@@ -195,11 +251,11 @@ tgetline(fd, buf, bufsiz, timeout)
 
            /* Make sure there is something to read (or timeout) */
            while ((n = select(fd + 1, readfds, 0, 0, &tv)) == -1 &&
-               (errno == EINTR || errno == EAGAIN))
+               errno == EAGAIN)
                ;
-           if (n == 0) {
+           if (n <= 0) {
                free(readfds);
-               return(NULL);           /* timeout */
+               return(NULL);           /* timeout or interrupt */
            }
 
            /* Read a character, exit loop on error, EOF or EOL */
@@ -216,5 +272,11 @@ tgetline(fd, buf, bufsiz, timeout)
     }
     *cp = '\0';
 
-    return(cp == buf ? NULL : buf);
+    return(n == -1 ? NULL : buf);
+}
+
+static void handler(s)
+    int s;
+{
+    signo = s;
 }