Module Name: src Committed By: christos Date: Thu Apr 12 22:07:44 UTC 2012
Modified Files: src/lib/libc/gen: getpass.3 getpass.c Log Message: add getpassfd() that gives us even more fine grain control on how to get the password. To generate a diff of this commit: cvs rdiff -u -r1.15 -r1.16 src/lib/libc/gen/getpass.3 cvs rdiff -u -r1.18 -r1.19 src/lib/libc/gen/getpass.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libc/gen/getpass.3 diff -u src/lib/libc/gen/getpass.3:1.15 src/lib/libc/gen/getpass.3:1.16 --- src/lib/libc/gen/getpass.3:1.15 Thu Apr 12 16:15:37 2012 +++ src/lib/libc/gen/getpass.3 Thu Apr 12 18:07:44 2012 @@ -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 te 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 Index: src/lib/libc/gen/getpass.c diff -u src/lib/libc/gen/getpass.c:1.18 src/lib/libc/gen/getpass.c:1.19 --- src/lib/libc/gen/getpass.c:1.18 Thu Apr 12 16:08:01 2012 +++ src/lib/libc/gen/getpass.c Thu Apr 12 18:07:44 2012 @@ -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/ #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, >) == -1) { + allocated = buf == NULL; + if (tcgetattr(fd[0], >) == -1) { havetty = false; -#ifdef DONT_WORK_AND_ECHO - goto out; -#else + if (flags & GETPASS_NEED_TTY) + goto out; memset(>, -1, sizeof(gt)); -#endif } else havetty = true; @@ -108,22 +103,33 @@ getpass_r(const char *prompt, char *ret, 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, /* 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, 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, /* 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,36 +201,78 @@ getpass_r(const char *prompt, char *ret, 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, >); - return ret; + (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, >); + return buf; restore: - c = errno; - if (havetty) - (void)tcsetattr(infd, TCSAFLUSH|TCSASOFT, >); - errno = c; + if (havetty) { + c = errno; + (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, >); + 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) { static char e[] = ""; @@ -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