add getpassfd() that gives us even more fine grain control on how to get

the password.
This commit is contained in:
christos 2012-04-12 22:07:44 +00:00
parent 0e4c610c3c
commit 969c948d8b
2 changed files with 151 additions and 56 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: getpass.3,v 1.15 2012/04/12 20:15:37 wiz Exp $
.\" $NetBSD: getpass.3,v 1.16 2012/04/12 22:07:44 christos Exp $
.\"
.\" Copyright (c) 1989, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@ -43,6 +43,8 @@
.Fn getpass "const char *prompt"
.Ft char *
.Fn getpass_r "const char *prompt" "char *buf" "size_t buflen"
.Ft char *
.Fn getpassfd "const char *prompt" "char *buf" "size_t buflen" "int fd[3]" "int flags"
.Sh DESCRIPTION
The
.Fn getpass
@ -71,6 +73,34 @@ only it puts its result in
for up to
.Fa buflen
characters.
If the
.Fa buf
argument is
.Dv NULL ,
then a buffer will be dynamically allocated.
.Pp
The
.Fn getpassfd
function allows one to specify the file descriptors used to be specified in
.Fa fd ,
and provides an extra
.Fa flags
argument to control its behavior:
.Bl -tag -width GETPASS_BUF_LIMIT
.It Dv GETPASS_NEED_TTY
Fail if we are unable to set the tty modes like we want.
.It Dv GETPASS_FAIL_EOF
Fail if we get the end-of-file character instead of returning the result so far.
.It Dv GETPASS_BUF_LIMIT
Beep when the buffer limit is reached, instead of silently absorbing it.
.It Dv GETPASS_NO_SIGNAL
Don't make ttychars send signals.
.It Dv GETPASS_NO_BEEP
Don't beep if we erase past the beginning of the buffer or we try to enter past
the end.
.It Dv GETPASS_ECHO
Echo characters as they are typed.
.El
.Sh RETURN VALUES
The
.Fn getpass
@ -78,7 +108,9 @@ 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
and
.Fn getpassfd
functions return a pointer to the NUL terminated password, or
.Dv NULL
on error.
.Sh FILES
@ -103,7 +135,9 @@ function appeared in
.At v7 .
The
.Fn getpass_r
function appeared in
and
.Fn getpassfd
functions appeared in
.Nx 7.0 .
.Sh BUGS
The

View File

@ -1,4 +1,4 @@
/* $NetBSD: getpass.c,v 1.18 2012/04/12 20:08:01 christos Exp $ */
/* $NetBSD: getpass.c,v 1.19 2012/04/12 22:07:44 christos Exp $ */
/*-
* Copyright (c) 2012 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: getpass.c,v 1.18 2012/04/12 20:08:01 christos Exp $");
__RCSID("$NetBSD: getpass.c,v 1.19 2012/04/12 22:07:44 christos Exp $");
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
@ -50,6 +50,7 @@ __RCSID("$NetBSD: getpass.c,v 1.18 2012/04/12 20:08:01 christos Exp $");
#include <fcntl.h>
#ifdef __weak_alias
__weak_alias(getpassfd,_getpassfd)
__weak_alias(getpass_r,_getpass_r)
__weak_alias(getpass,_getpass)
#endif
@ -58,46 +59,40 @@ __weak_alias(getpass,_getpass)
* 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.
* tunable for that GETPASS_FAIL_EOF to disable this.
* - Historically getpass ate extra characters silently, we provide
* a tunable for that DONT_DISCARD_SILENTLY to disable this.
* a tunable for that GETPASS_BUF_LIMIT to disable this.
* - Historically getpass "worked" by echoing characters when turning
* off echo failed, we provide a tunable DONT_WORK_AND_ECHO to
* off echo failed, we provide a tunable GETPASS_NEED_TTY to
* disable this.
* - Some implementations say that on interrupt the program shall
* receive an interrupt signal before the function returns. We
* send all the tty signals before we return, but we don't expect
* suspend to do something useful unless the caller calls us again.
* We also provide a tunable to disable signal delivery
* GETPASS_NO_SIGNAL.
* - GETPASS_NO_BEEP disables beeping.
* - GETPASS_ECHO will echo the password (as pam likes it)
*/
char *
getpass_r(const char *prompt, char *ret, size_t len)
/*ARGSUSED*/
getpassfd(const char *prompt, char *buf, size_t len, int fd[], int flags)
{
struct termios gt;
char c;
int infd, outfd, sig;
bool lnext, havetty;
int sig;
bool lnext, havetty, allocated;
_DIAGASSERT(prompt != NULL);
sig = 0;
/*
* Try to use /dev/tty if possible; otherwise read from stdin and
* write to stderr.
*/
if ((outfd = infd = open(_PATH_TTY, O_RDWR)) == -1) {
infd = STDIN_FILENO;
outfd = STDERR_FILENO;
havetty = false;
} else
havetty = true;
if (tcgetattr(infd, &gt) == -1) {
allocated = buf == NULL;
if (tcgetattr(fd[0], &gt) == -1) {
havetty = false;
#ifdef DONT_WORK_AND_ECHO
goto out;
#else
if (flags & GETPASS_NEED_TTY)
goto out;
memset(&gt, -1, sizeof(gt));
#endif
} else
havetty = true;
@ -108,22 +103,33 @@ getpass_r(const char *prompt, char *ret, size_t len)
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)
if (tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &st) == -1)
goto out;
}
if (prompt != NULL) {
size_t plen = strlen(prompt);
(void)write(outfd, prompt, plen);
(void)write(fd[1], prompt, plen);
}
if (allocated) {
len = 1024;
if ((buf = malloc(len)) == NULL)
goto restore;
}
c = '\1';
lnext = false;
for (size_t l = 0; c != '\0'; ) {
if (read(infd, &c, 1) != 1)
if (read(fd[0], &c, 1) != 1)
goto restore;
#define beep() write(outfd, "\a", 1)
#define beep() do \
if (flags & GETPASS_NO_BEEP) \
(void)write(fd[2], "\a", 1); \
while (/*CONSTCOND*/ 0)
#define erase() (void)write(fd[1], "\b \b", 3)
#define C(a, b) (gt.c_cc[(a)] == _POSIX_VDISABLE ? (b) : gt.c_cc[(a)])
if (lnext) {
@ -145,6 +151,10 @@ getpass_r(const char *prompt, char *ret, size_t len)
/* Line or word kill, treat as reset */
if (c == C(VKILL, CTRL('u')) || c == C(VWERASE, CTRL('w'))) {
if (flags & GETPASS_ECHO) {
while (l--)
erase();
}
l = 0;
continue;
}
@ -153,8 +163,11 @@ getpass_r(const char *prompt, char *ret, size_t len)
if (c == C(VERASE, CTRL('h'))) {
if (l == 0)
beep();
else
else {
l--;
if (flags & GETPASS_ECHO)
erase();
}
continue;
}
@ -174,13 +187,13 @@ getpass_r(const char *prompt, char *ret, size_t len)
/* EOF */
if (c == C(VEOF, CTRL('d'))) {
#ifdef DONT_TREAT_EOF_AS_EOL
errno = ENODATA;
goto out;
#else
c = '\0';
goto add;
#endif
if (flags & GETPASS_FAIL_EOF) {
errno = ENODATA;
goto out;
} else {
c = '\0';
goto add;
}
}
/* End of line */
@ -188,35 +201,77 @@ getpass_r(const char *prompt, char *ret, size_t len)
c = '\0';
add:
if (l >= len) {
#ifdef DONT_DISCARD_SILENTLY
beep();
continue;
#else
if (c == '\0' && l > 0)
l--;
else
continue;
#endif
if (allocated) {
len += 1024;
char *b = realloc(buf, len);
if (b == NULL)
goto restore;
buf = b;
} else {
if (flags & GETPASS_BUF_LIMIT) {
beep();
continue;
}
if (c == '\0' && l > 0)
l--;
else
continue;
}
}
ret[l++] = c;
buf[l++] = c;
if (c && (flags & GETPASS_ECHO))
(void)write(fd[1], &c, 1);
}
if (havetty)
(void)tcsetattr(infd, TCSAFLUSH|TCSASOFT, &gt);
return ret;
(void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &gt);
return buf;
restore:
c = errno;
if (havetty)
(void)tcsetattr(infd, TCSAFLUSH|TCSASOFT, &gt);
errno = c;
if (havetty) {
c = errno;
(void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &gt);
errno = c;
}
out:
if (sig) {
(void)raise(sig);
if ((flags & GETPASS_NO_SIGNAL) == 0)
(void)raise(sig);
errno = EINTR;
}
memset(buf, 0, len);
if (allocated)
free(buf);
return NULL;
}
char *
getpass_r(const char *prompt, char *buf, size_t len)
{
bool opentty;
int fd[3];
char *rv;
/*
* Try to use /dev/tty if possible; otherwise read from stdin and
* write to stderr.
*/
if ((fd[0] = fd[1] = fd[2] = open(_PATH_TTY, O_RDWR)) == -1) {
opentty = false;
fd[0] = STDIN_FILENO;
fd[1] = fd[2] = STDERR_FILENO;
} else
opentty = true;
rv = getpassfd(prompt, buf, len, fd, 0);
if (opentty) {
int serrno = errno;
(void)close(fd[0]);
errno = serrno;
}
return rv;
}
char *
getpass(const char *prompt)
{
@ -225,6 +280,11 @@ getpass(const char *prompt)
static long bufsiz;
char *rv;
/*
* Strictly speaking we could double allocate here, if we get
* called at the same time, but this function is not re-entrant
* anyway and it is not supposed to work if called concurrently.
*/
if (buf == NULL) {
if ((bufsiz = sysconf(_SC_PASS_MAX)) == -1)
return e;
@ -243,7 +303,8 @@ int
main(int argc, char *argv[])
{
char buf[28];
printf("[%s]\n", getpass_r("foo>", buf, sizeof(buf)));
int fd[3] = { 0, 1, 2 };
printf("[%s]\n", getpassfd("foo>", buf, sizeof(buf), fd, GETPASS_ECHO));
return 0;
}
#endif