Add a new getpass implementation that does not mess with signals, and

include getpass_r
This commit is contained in:
christos 2012-04-12 19:36:19 +00:00
parent 7111124bac
commit 48c7acbcd7
4 changed files with 234 additions and 76 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile.inc,v 1.182 2012/02/11 23:31:24 martin Exp $
# $NetBSD: Makefile.inc,v 1.183 2012/04/12 19:36:19 christos Exp $
# from: @(#)Makefile.inc 8.6 (Berkeley) 5/4/95
# gen sources
@ -117,6 +117,7 @@ MLINKS+=cgetcap.3 cgetclose.3 cgetcap.3 cgetent.3 \
cgetcap.3 cgetnum.3 cgetcap.3 cgetset.3 cgetcap.3 cgetstr.3 \
cgetcap.3 cgetustr.3
MLINKS+=getcwd.3 getwd.3
MLINKS+=getpass.3 getpass_r.3
MLINKS+=getdiskbyname.3 setdisktab.3
MLINKS+=getdomainname.3 setdomainname.3
MLINKS+=getfsent.3 endfsent.3 getfsent.3 getfsfile.3 getfsent.3 getfsspec.3 \

View File

@ -1,4 +1,4 @@
.\" $NetBSD: getpass.3,v 1.13 2010/05/06 11:09:39 jruoho Exp $
.\" $NetBSD: getpass.3,v 1.14 2012/04/12 19:36:19 christos Exp $
.\"
.\" Copyright (c) 1989, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\"
.\" @(#)getpass.3 8.1 (Berkeley) 6/4/93
.\"
.Dd May 6, 2010
.Dd April 12, 2012
.Dt GETPASS 3
.Os
.Sh NAME
@ -38,10 +38,11 @@
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In pwd.h
.In unistd.h
.Ft char *
.Fn getpass "const char *prompt"
.Ft char *
.Fn getpass_r "const char *prompt" "char *buf" "size_t buflen"
.Sh DESCRIPTION
The
.Fn getpass
@ -52,16 +53,34 @@ If this file is not accessible,
displays the prompt on the standard error output and reads from the standard
input.
.Pp
The password may be up to _PASSWORD_LEN (currently 128)
The password may be up to
.Xr sysconf 3
.Dv _SC_PASS_MAX
characters in length.
Any additional
characters and the terminating newline character are discarded.
.Pp
.Fn getpass
turns off character echoing while reading the password.
.Sh RETURN VALUES
.Pp
.Fn getpass_r
is similar to
.Fn getpass
returns a pointer to the null terminated password.
only it puts its result in
.Fa buf
for up to
.Fa buflen
characters.
.Sh RETURN VALUES
The
.Fn getpass
function returns a pointer to the NUL terminated password, or an empty
string on error.
The
.Fn getpass_r
function returns a pointer to the NUL terminated password, or
.Dv NULL
on error.
.Sh FILES
.Bl -tag -width /dev/tty -compact
.It Pa /dev/tty
@ -82,6 +101,10 @@ A
.Fn getpass
function appeared in
.At v7 .
The
.Fn getpass_r
function appeared in
.Nx 7.0 .
.Sh BUGS
The
.Fn getpass
@ -94,3 +117,9 @@ will modify the same object.
The calling process should zero the password as soon as possible to
avoid leaving the cleartext password visible in the process's address
space.
.Pp
Historically
.Nm
accepted and returned a password if it could not modify the terminal
settings to turn echo off (or if the input was not a terminal).
In this implementation, only terminal input is accepted.

View File

@ -1,8 +1,11 @@
/* $NetBSD: getpass.c,v 1.16 2008/01/01 21:22:55 christos Exp $ */
/* $NetBSD: getpass.c,v 1.17 2012/04/12 19:36:19 christos Exp $ */
/*
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
/*-
* Copyright (c) 2012 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
@ -12,96 +15,220 @@
* 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. Neither the name of the University 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 REGENTS 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 REGENTS 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.
* 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)
#if 0
static char sccsid[] = "@(#)getpass.c 8.1 (Berkeley) 6/4/93";
#else
__RCSID("$NetBSD: getpass.c,v 1.16 2008/01/01 21:22:55 christos Exp $");
#endif
__RCSID("$NetBSD: getpass.c,v 1.17 2012/04/12 19:36:19 christos Exp $");
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include <assert.h>
#include <paths.h>
#include <pwd.h>
#include <signal.h>
#ifdef TEST
#include <stdio.h>
#endif
#include <errno.h>
#include <string.h>
#include <paths.h>
#include <stdbool.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef __weak_alias
__weak_alias(getpass_r,_getpass_r)
__weak_alias(getpass,_getpass)
#endif
/*
* Notes:
* - There is no getpass_r in POSIX
* - Historically EOF is documented to be treated as EOL, we provide a
* tunable for that DONT_TREAT_EOF_AS_EOL to disable this.
* - Historically getpass ate extra characters silently, we provide
* a tunable for that DONT_DISCARD_SILENTLY to disable this.
* - Historically getpass "worked" by echoing characters when turning
* off echo failed, we provide a tunable DONT_WORK_AND_ECHO to
* disable this.
* - Some implementations say that on interrupt the program shall
* receive an interrupt signal before the function returns. This
* does not sound useful, but it could be easy to implement using
* raise(3).
*/
char *
getpass(prompt)
const char *prompt;
getpass_r(const char *prompt, char *ret, size_t len)
{
struct termios term;
int ch;
char *p;
FILE *fp, *outfp;
int echo;
static char buf[_PASSWORD_LEN + 1];
sigset_t oset, nset;
struct termios gt;
char c;
int infd, outfd;
bool lnext, havetty;
_DIAGASSERT(prompt != NULL);
/*
* note - blocking signals isn't necessarily the
* right thing, but we leave it for now.
* Try to use /dev/tty if possible; otherwise read from stdin and
* write to stderr.
*/
sigemptyset(&nset);
sigaddset(&nset, SIGINT);
sigaddset(&nset, SIGTSTP);
(void)sigprocmask(SIG_BLOCK, &nset, &oset);
if ((outfd = infd = open(_PATH_TTY, O_RDWR)) == -1) {
infd = STDIN_FILENO;
outfd = STDERR_FILENO;
havetty = false;
} else
havetty = true;
/*
* read and write to /dev/tty if possible; else read from
* stdin and write to stderr.
*/
if ((outfp = fp = fopen(_PATH_TTY, "w+")) == NULL) {
outfp = stderr;
fp = stdin;
if (tcgetattr(infd, &gt) == -1) {
havetty = false;
#ifdef DONT_WORK_AND_ECHO
goto out;
#else
memset(&gt, -1, sizeof(gt));
#endif
} else
havetty = true;
if (havetty) {
struct termios st = gt;
st.c_lflag &= ~(ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL|ISIG|ICANON);
st.c_cc[VMIN] = 1;
st.c_cc[VTIME] = 0;
if (tcsetattr(infd, TCSAFLUSH|TCSASOFT, &st) == -1)
goto out;
}
(void)tcgetattr(fileno(fp), &term);
if ((echo = (term.c_lflag & ECHO)) != 0) {
term.c_lflag &= ~ECHO;
(void)tcsetattr(fileno(fp), TCSAFLUSH|TCSASOFT, &term);
if (prompt != NULL) {
size_t plen = strlen(prompt);
(void)write(outfd, prompt, plen);
}
if (prompt != NULL)
(void)fputs(prompt, outfp);
rewind(outfp); /* implied flush */
for (p = buf; (ch = getc(fp)) != EOF && ch != '\n';)
if (p < buf + _PASSWORD_LEN)
*p++ = ch;
*p = '\0';
(void)write(fileno(outfp), "\n", 1);
if (echo) {
term.c_lflag |= ECHO;
(void)tcsetattr(fileno(fp), TCSAFLUSH|TCSASOFT, &term);
c = '\1';
lnext = false;
for (size_t l = 0; c != '\0'; ) {
if (read(infd, &c, 1) != 1)
goto restore;
#define beep() write(outfd, "\a", 1)
#define C(a, b) (gt.c_cc[(a)] == _POSIX_VDISABLE ? (b) : gt.c_cc[(a)])
if (lnext) {
lnext = false;
goto add;
}
/* Ignored */
if (c == C(VREPRINT, CTRL('r')) || c == C(VSTART, CTRL('q')) ||
c == C(VSTOP, CTRL('s')) || c == C(VSTATUS, CTRL('t')) ||
c == C(VDISCARD, CTRL('o')))
continue;
/* Literal next */
if (c == C(VLNEXT, CTRL('v'))) {
lnext = true;
continue;
}
/* Line or word kill, treat as reset */
if (c == C(VKILL, CTRL('u')) || c == C(VWERASE, CTRL('w'))) {
l = 0;
continue;
}
/* Character erase */
if (c == C(VERASE, CTRL('h'))) {
if (l == 0)
beep();
else
l--;
continue;
}
/* EOF or tty signal characters */
if (
#ifdef DONT_TREAT_EOF_AS_EOL
c == C(VEOF, CTRL('d')) ||
#endif
c == C(VINTR, CTRL('c')) ||
c == C(VQUIT, CTRL('\\')) || c == C(VSUSP, CTRL('z')) ||
c == C(VDSUSP, CTRL('y'))) {
errno = EINTR;
goto out;
}
/* End of line */
if (
#ifndef DONT_TREAT_EOF_AS_EOL
c == C(VEOF, CTRL('d')) ||
#endif
c == C(VEOL, CTRL('j')) || c == C(VEOL2, CTRL('l')))
c = '\0';
add:
if (l >= len) {
#ifdef DONT_DISCARD_SILENTLY
beep();
continue;
#else
if (c == '\0' && l > 0)
l--;
else
continue;
#endif
}
ret[l++] = c;
}
if (fp != stdin)
(void)fclose(fp);
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
return(buf);
if (havetty)
(void)tcsetattr(infd, TCSAFLUSH|TCSASOFT, &gt);
return ret;
restore:
c = errno;
if (havetty)
(void)tcsetattr(infd, TCSAFLUSH|TCSASOFT, &gt);
errno = c;
out:
return NULL;
}
char *
getpass(const char *prompt)
{
static char e[] = "";
static char *buf;
static long bufsiz;
char *rv;
if (buf == NULL) {
if ((bufsiz = sysconf(_SC_PASS_MAX)) == -1)
return e;
if ((buf = malloc((size_t)bufsiz)) == NULL)
return e;
}
if ((rv = getpass_r(prompt, buf, (size_t)bufsiz)) == NULL)
return e;
return rv;
}
#ifdef TEST
int
main(int argc, char *argv[])
{
char buf[28];
printf("[%s]\n", getpass_r("foo>", buf, sizeof(buf)));
return 0;
}
#endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: namespace.h,v 1.151 2012/03/02 17:27:49 christos Exp $ */
/* $NetBSD: namespace.h,v 1.152 2012/04/12 19:36:19 christos Exp $ */
/*-
* Copyright (c) 1997-2004 The NetBSD Foundation, Inc.
@ -326,6 +326,7 @@
#define getopt_long _getopt_long
#define getpagesize _getpagesize
#define getpass _getpass
#define getpass_r _getpass_r
#define getprogname _getprogname
#define getprotobyname _getprotobyname
#define getprotobyname_r _getprotobyname_r