Subject: lib/10955: getpass(3) implementations are inconsistent
To: None <gnats-bugs@gnats.netbsd.org>
From: None <christos@zoulas.com>
List: netbsd-bugs
Date: 09/05/2000 10:39:20
>Number: 10955
>Category: lib
>Synopsis: libc getpass(3) blocks signals.
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: lib-bug-people
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Tue Sep 05 10:40:00 PDT 2000
>Closed-Date:
>Last-Modified:
>Originator: Christos Zoulas
>Release: Tue Sep 5 13:26:41 EDT 2000
>Organization:
None, but I am trying.
>Environment:
System: NetBSD hrothgar.gw.com 1.4.2A NetBSD 1.4.2A (GW-GENERIC) #6: Wed May 31 06:12:46 EEST 2000 kim@pyry.gw.com:/net/pyry/src-2/NetBSD/cvsroot/src/sys/arch/i386/compile/GW-GENERIC i386
>Description:
Unfortunately many programs that need getpass(3) [su, kerberos,
ssh] don't use the standard getpass(3) implementation from libc,
but roll their own leasing to inconsistent behavior. All other
getpass(3) implementations trap signals, but the libc one does
not. This getpass implementation, traps signals and uses raw
reads/writes and non-canonical tty processing to achive the
desired result.
I.e.
% su
Password: 123^Z
Suspended
% fg
456\n
# makes the su program see 123456 as the password.
>How-To-Repeat:
Hit control-C or control-Z when typing a password.
>Fix:
Replace getpass() with the following implementation.
/* $NetBSD$ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Christos Zoulas.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD$");
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include <assert.h>
#include <paths.h>
#include <pwd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#ifdef __weak_alias
__weak_alias(getpass,_getpass)
#endif
static void sighandler __P((int));
/*
* This is not re-entrant anyway because of the static buffer
* so we use static variables to communicate, since there is
* currently no way to pass data to and from signal handlers.
*/
static int gotsig = -1;
static void
sighandler(n)
int n;
{
switch (n) {
case SIGTSTP:
case SIGINT:
gotsig = n;
return;
default:
/* abort? */
break;
}
}
char *
getpass(prompt)
const char *prompt;
{
int nr;
char *p;
int infd, outfd;
static char buf[_PASSWORD_LEN + 1];
char *bufp = NULL;
struct sigaction osaint, osatstp, nsa;
struct termios nterm, oterm;
_DIAGASSERT(prompt != NULL);
gotsig = -1;
/*
* read and write to /dev/tty if possible; else read from
* stdin and write to stderr.
*/
if ((infd = outfd = open(_PATH_TTY, O_RDWR)) == -1) {
outfd = STDERR_FILENO;
infd = STDIN_FILENO;
}
if (tcgetattr(infd, &oterm) == -1)
goto cleanup0;
nsa.sa_handler = sighandler;
nsa.sa_flags = 0;
sigemptyset(&nsa.sa_mask);
if (sigaction(SIGINT, &nsa, &osaint) == -1)
goto cleanup0;
if (sigaction(SIGTSTP, &nsa, &osatstp) == -1)
goto cleanup1;
nterm = oterm;
nterm.c_lflag &= ~(ECHO|ICANON);
nterm.c_cc[VMIN] = 1;
nterm.c_cc[VTIME] = 0;
if (tcsetattr(infd, TCSAFLUSH|TCSASOFT, &nterm) == -1)
goto cleanup2;
if (prompt != NULL)
(void)write(outfd, prompt, strlen(prompt));
for (p = buf; p < buf + _PASSWORD_LEN; p++) {
while ((nr = read(infd, p, 1)) == -1 && errno == EINTR) {
(void)tcsetattr(infd, TCSAFLUSH|TCSASOFT, &oterm);
(void)sigaction(SIGTSTP, &osatstp, NULL);
(void)sigaction(SIGINT, &osaint, NULL);
(void)kill(0, gotsig);
gotsig = -1;
(void)sigaction(SIGINT, &nsa, NULL);
(void)sigaction(SIGTSTP, &nsa, NULL);
(void)tcsetattr(infd, TCSAFLUSH|TCSASOFT, &nterm);
}
if (nr == -1 || *p == '\n' || *p == oterm.c_cc[VEOF])
break;
if (*p == oterm.c_cc[VERASE] && p >= buf)
p -= 2;
else if (*p == oterm.c_cc[VKILL])
p = buf - 1;
else if (*p == oterm.c_cc[VREPRINT] && p >= buf)
p--;
/* XXX: we don't handle VWERASE, VLNEXT, VSTART, VSTOP etc. */
}
*p = '\0';
(void)write(outfd, "\n", 1);
bufp = buf;
(void)tcsetattr(infd, TCSAFLUSH|TCSASOFT, &nterm);
cleanup2:
(void)sigaction(SIGTSTP, &osatstp, NULL);
cleanup1:
(void)sigaction(SIGINT, &osaint, NULL);
cleanup0:
if (infd == outfd && infd != -1)
(void)close(infd);
return(bufp);
}
#ifdef TEST
static void sigtest __P((int));
int main __P((int, char *[]));
static void
sigtest(n)
int n;
{
printf("got signal %d\n", n);
}
int
main(argc, argv)
int argc;
char *argv[];
{
struct sigaction sa;
char *foo;
foo = getpass("notrap:");
printf("notrap = %s\n", foo);
sa.sa_handler = sigtest;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
(void) sigaction(SIGINT, &sa, NULL);
(void) sigaction(SIGTSTP, &sa, NULL);
foo = getpass("trap:");
printf("trap = %s\n", foo);
return 0;
}
#endif
>Release-Note:
>Audit-Trail:
>Unformatted: