Subject: lib/28505: getpass does not restore terminal after SIGCONT
To: None <lib-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <Peter.Bex@student.kun.nl>
List: netbsd-bugs
Date: 12/01/2004 23:12:00
>Number: 28505
>Category: lib
>Synopsis: Sometimes getpass(3) echoes password chars after SIGSTOP + SIGCONT
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Dec 01 23:12:00 +0000 2004
>Originator: Peter Bex
>Release: NetBSD 2.0_RC1
>Organization:
>Environment:
System: NetBSD frohike.nvie.com 2.0_RC1 NetBSD 2.0_RC1 (FROHIKE) #3: Wed Sep 29 19:34:49 CEST 2004 root@:/usr/src/sys/arch/i386/compile/FROHIKE i386
Architecture: i386
Machine: i386
>Description:
When running su, suspending that by sending SIGSTOP and then resuming
it again, in some shells (those that don't save/restore the child
process' terminal state correctly) the password which is typed is
echoed back to the terminal.
This appears to be a bug in how getpass(3) handles signals.
>How-To-Repeat:
$ /usr/pkg/bin/zsh
% su
Password:
<switch to other terminal>
$ pkill -SIGSTOP su
<switch back to su terminal>
Password:zsh: suspended (signal) su
% fg
[1] + continued su
<Password is now echoed to the terminal>
I was only able to reproduce this problem under the zsh.
(pkgsrc/shells/zsh, pkg version 4.2.1)
/bin/csh and /bin/ksh don't have this problem (presumably because
these shells save and restore child process terminal states)
On a related note, /bin/sh does not do anything about the terminal,
so when you start typing after sending the STOP signal to su,
the characters aren't echoed.
>Fix:
Handle SIGSTOP and SIGCONT and set terminal state there.
--- getpass.c.orig 2003-08-07 18:42:50.000000000 +0200
+++ getpass.c 2004-12-01 22:34:21.000000000 +0100
@@ -52,6 +52,28 @@
__weak_alias(getpass,_getpass)
#endif
+/* Reset terminal on suspend/resume of the job, so pwd will not be echoed */
+static void sighan(int sig)
+{
+ struct termios term;
+ FILE *fp;
+
+ if ((fp = fopen(_PATH_TTY, "w+")) == NULL)
+ fp = stdin;
+
+ (void)tcgetattr(fileno(fp), &term);
+
+ if (sig == SIGCONT)
+ term.c_lflag &= ~ECHO;
+ else
+ term.c_lflag |= ECHO;
+
+ (void)tcsetattr(fileno(fp), TCSAFLUSH|TCSASOFT, &term);
+
+ if (fp != stdin)
+ (void)fclose(fp);
+}
+
char *
getpass(prompt)
const char *prompt;
@@ -63,6 +85,7 @@
int echo;
static char buf[_PASSWORD_LEN + 1];
sigset_t oset, nset;
+ struct sigaction act, ostop_act, ocont_act;
_DIAGASSERT(prompt != NULL);
@@ -84,6 +107,12 @@
sigaddset(&nset, SIGTSTP);
(void)sigprocmask(SIG_BLOCK, &nset, &oset);
+ sigemptyset(&act.sa_mask);
+ act.sa_handler = sighan;
+ act.sa_flags = 0;
+ sigaction(SIGSTOP, &act, &ostop_act);
+ sigaction(SIGCONT, &act, &ocont_act);
+
(void)tcgetattr(fileno(fp), &term);
if ((echo = (term.c_lflag & ECHO)) != 0) {
term.c_lflag &= ~ECHO;
@@ -101,6 +130,8 @@
term.c_lflag |= ECHO;
(void)tcsetattr(fileno(fp), TCSAFLUSH|TCSASOFT, &term);
}
+ sigaction(SIGSTOP, &ostop_act, NULL);
+ sigaction(SIGCONT, &ocont_act, NULL);
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
if (fp != stdin)
(void)fclose(fp);
>Unformatted: