diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2001-12-06 05:20:51 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2001-12-06 05:20:51 +0000 |
commit | 7379aa77a00dba03363eb1419cf4ae4e335d830f (patch) | |
tree | d25422615b4827ef97a3324391a69a262e06baec /lib/libc/gen | |
parent | 8674f2f60e0970e494f3a26da350e24a135ec756 (diff) |
Fix a long standing annoyance with getpass/readpassphrase. Instead of
blocking SIGINT and SIGTSTP, catch them (along with SIGHUP, SIGQUIT, SIGTERM).
We restore the tty mode as needed and then restore the original signal
handler and resend the signal. For SIGTSTP, upon return from suspend
the user is re-prompted for the passphrase.
Diffstat (limited to 'lib/libc/gen')
-rw-r--r-- | lib/libc/gen/readpassphrase.c | 107 |
1 files changed, 87 insertions, 20 deletions
diff --git a/lib/libc/gen/readpassphrase.c b/lib/libc/gen/readpassphrase.c index 7a77fb85d97..a8ca0875fa0 100644 --- a/lib/libc/gen/readpassphrase.c +++ b/lib/libc/gen/readpassphrase.c @@ -1,3 +1,5 @@ +/* $OpenBSD: readpassphrase.c,v 1.8 2001/12/06 05:20:50 millert Exp $ */ + /* * Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com> * All rights reserved. @@ -26,7 +28,7 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$OpenBSD: readpassphrase.c,v 1.7 2001/08/07 19:34:11 millert Exp $"; +static const char rcsid[] = "$OpenBSD: readpassphrase.c,v 1.8 2001/12/06 05:20:50 millert Exp $"; #endif /* LIBC_SCCS and not lint */ #include <ctype.h> @@ -40,17 +42,19 @@ static const char rcsid[] = "$OpenBSD: readpassphrase.c,v 1.7 2001/08/07 19:34:1 #include <unistd.h> #include <readpassphrase.h> +/* Shared with signal handler below to restore state. */ +static struct termios term, oterm; +static struct sigaction sa, saveint, savehup, savequit, saveterm, savetstp; +static int input; +static volatile sig_atomic_t susp; + +static void handler(int); + char * -readpassphrase(prompt, buf, bufsiz, flags) - const char *prompt; - char *buf; - size_t bufsiz; - int flags; +readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) { - struct termios term, oterm; char ch, *p, *end; - int input, output; - sigset_t oset, nset; + int output; /* I suppose we could alloc on demand in this case (XXX). */ if (bufsiz == 0) { @@ -72,14 +76,19 @@ readpassphrase(prompt, buf, bufsiz, flags) } /* - * We block SIGINT and SIGTSTP so the terminal is not left - * in an inconsistent state (ie: no echo). It would probably - * be better to simply catch these though. + * 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(&nset); - sigaddset(&nset, SIGINT); - sigaddset(&nset, SIGTSTP); - (void)sigprocmask(SIG_BLOCK, &nset, &oset); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = handler; + (void)sigaction(SIGINT, &sa, &saveint); + (void)sigaction(SIGHUP, &sa, &savehup); + (void)sigaction(SIGQUIT, &sa, &savequit); + (void)sigaction(SIGTERM, &sa, &saveterm); + sa.sa_flags = 0; /* don't restart for SIGTSTP */ + (void)sigaction(SIGTSTP, &sa, &savetstp); /* Turn off echo if possible. */ if (tcgetattr(input, &oterm) == 0) { @@ -94,6 +103,7 @@ readpassphrase(prompt, buf, bufsiz, flags) memset(&oterm, 0, sizeof(oterm)); } +redo: (void)write(output, prompt, strlen(prompt)); end = buf + bufsiz - 1; for (p = buf; read(input, &ch, 1) == 1 && ch != '\n' && ch != '\r';) { @@ -109,24 +119,81 @@ readpassphrase(prompt, buf, bufsiz, flags) *p++ = ch; } } + if (susp) { + /* Back from suspend. */ + susp = 0; + goto redo; + } *p = '\0'; if (!(term.c_lflag & ECHO)) (void)write(output, "\n", 1); - /* Restore old terminal settings and signal mask. */ + /* Restore old terminal settings and signals. */ if (memcmp(&term, &oterm, sizeof(term)) != 0) (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm); - (void)sigprocmask(SIG_SETMASK, &oset, NULL); + (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); } char * -getpass(prompt) - const char *prompt; +getpass(const char *prompt) { static char buf[_PASSWORD_LEN + 1]; return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF)); } + +static void handler(int signo) +{ + struct sigaction osa; + sigset_t nset; + int save_errno; + + save_errno = errno; + + /* Restore tty modes */ + if (memcmp(&term, &oterm, sizeof(term)) != 0) + (void)tcsetattr(input, TCSANOW|TCSASOFT, &oterm); + + /* + * Save old handler and set to original value. + * Unblock receipt of 'signo' and resend the signal so that + * it is caught by the pre-readpassphrase handler. + */ + switch (signo) { + case SIGINT: + (void)sigaction(signo, &saveint, &osa); + break; + case SIGHUP: + (void)sigaction(signo, &savehup, &osa); + break; + case SIGQUIT: + (void)sigaction(signo, &savequit, &osa); + break; + case SIGTERM: + (void)sigaction(signo, &saveterm, &osa); + break; + case SIGTSTP: + (void)sigaction(signo, &savetstp, &osa); + susp = 1; + break; + } + (void)sigemptyset(&nset); + (void)sigaddset(&nset, signo); + (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); + (void)kill(getpid(), signo); + (void)sigprocmask(SIG_BLOCK, &nset, NULL); + (void)sigaction(signo, &osa, NULL); + + /* Put tty modes back */ + if (memcmp(&term, &oterm, sizeof(term)) != 0) + (void)tcsetattr(input, TCSANOW|TCSASOFT, &term); + + errno = save_errno; +} |