1*8397619cSchristos /* $NetBSD: getpass.c,v 1.30 2016/01/31 23:41:38 christos Exp $ */
2b585e843Scgd
348c7acbcSchristos /*-
448c7acbcSchristos * Copyright (c) 2012 The NetBSD Foundation, Inc.
548c7acbcSchristos * All rights reserved.
648c7acbcSchristos *
748c7acbcSchristos * This code is derived from software contributed to The NetBSD Foundation
848c7acbcSchristos * by Christos Zoulas.
961f28255Scgd *
1061f28255Scgd * Redistribution and use in source and binary forms, with or without
1161f28255Scgd * modification, are permitted provided that the following conditions
1261f28255Scgd * are met:
1361f28255Scgd * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd * notice, this list of conditions and the following disclaimer.
1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd * notice, this list of conditions and the following disclaimer in the
1761f28255Scgd * documentation and/or other materials provided with the distribution.
1861f28255Scgd *
1948c7acbcSchristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2048c7acbcSchristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2148c7acbcSchristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2248c7acbcSchristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2348c7acbcSchristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2448c7acbcSchristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2548c7acbcSchristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2648c7acbcSchristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2748c7acbcSchristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2848c7acbcSchristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2948c7acbcSchristos * POSSIBILITY OF SUCH DAMAGE.
3061f28255Scgd */
313e458a62Schristos #include <sys/cdefs.h>
3261f28255Scgd #if defined(LIBC_SCCS) && !defined(lint)
33*8397619cSchristos __RCSID("$NetBSD: getpass.c,v 1.30 2016/01/31 23:41:38 christos Exp $");
3461f28255Scgd #endif /* LIBC_SCCS and not lint */
3561f28255Scgd
3643fa6fe3Sjtc #include "namespace.h"
37b585e843Scgd
38b48252f3Slukem #include <assert.h>
3948c7acbcSchristos #ifdef TEST
4061f28255Scgd #include <stdio.h>
4148c7acbcSchristos #endif
4248c7acbcSchristos #include <errno.h>
4398ba7d20Schristos #include <ctype.h>
4410fa0b64Schristos #include <signal.h>
4548c7acbcSchristos #include <string.h>
4648c7acbcSchristos #include <paths.h>
4748c7acbcSchristos #include <stdbool.h>
4848c7acbcSchristos #include <stdlib.h>
49b48252f3Slukem #include <termios.h>
5061f28255Scgd #include <unistd.h>
5148c7acbcSchristos #include <fcntl.h>
5277cac556Schristos #include <poll.h>
5361f28255Scgd
5443fa6fe3Sjtc #ifdef __weak_alias
__weak_alias(getpassfd,_getpassfd)55969c948dSchristos __weak_alias(getpassfd,_getpassfd)
5648c7acbcSchristos __weak_alias(getpass_r,_getpass_r)
5760549036Smycroft __weak_alias(getpass,_getpass)
5843fa6fe3Sjtc #endif
5943fa6fe3Sjtc
6048c7acbcSchristos /*
6148c7acbcSchristos * Notes:
6248c7acbcSchristos * - There is no getpass_r in POSIX
6348c7acbcSchristos * - Historically EOF is documented to be treated as EOL, we provide a
64969c948dSchristos * tunable for that GETPASS_FAIL_EOF to disable this.
6548c7acbcSchristos * - Historically getpass ate extra characters silently, we provide
66969c948dSchristos * a tunable for that GETPASS_BUF_LIMIT to disable this.
6748c7acbcSchristos * - Historically getpass "worked" by echoing characters when turning
68969c948dSchristos * off echo failed, we provide a tunable GETPASS_NEED_TTY to
6948c7acbcSchristos * disable this.
7048c7acbcSchristos * - Some implementations say that on interrupt the program shall
7110fa0b64Schristos * receive an interrupt signal before the function returns. We
7210fa0b64Schristos * send all the tty signals before we return, but we don't expect
7310fa0b64Schristos * suspend to do something useful unless the caller calls us again.
74969c948dSchristos * We also provide a tunable to disable signal delivery
75969c948dSchristos * GETPASS_NO_SIGNAL.
76969c948dSchristos * - GETPASS_NO_BEEP disables beeping.
77311b90b9Schristos * - GETPASS_ECHO_STAR will echo '*' for each character of the password
78969c948dSchristos * - GETPASS_ECHO will echo the password (as pam likes it)
793765cebfSchristos * - GETPASS_7BIT strips the 8th bit
803765cebfSchristos * - GETPASS_FORCE_UPPER forces to uppercase
813765cebfSchristos * - GETPASS_FORCE_LOWER forces to uppercase
823765cebfSchristos * - GETPASS_ECHO_NL echo's a new line on success if echo was off.
8348c7acbcSchristos */
8461f28255Scgd char *
85969c948dSchristos /*ARGSUSED*/
863765cebfSchristos getpassfd(const char *prompt, char *buf, size_t len, int *fd, int flags,
8777cac556Schristos int tout)
8861f28255Scgd {
8948c7acbcSchristos struct termios gt;
9048c7acbcSchristos char c;
91969c948dSchristos int sig;
923765cebfSchristos bool lnext, havetty, allocated, opentty, good;
933765cebfSchristos int fdc[3];
9461f28255Scgd
95b48252f3Slukem _DIAGASSERT(prompt != NULL);
96b48252f3Slukem
973765cebfSchristos if (buf != NULL && len == 0) {
983765cebfSchristos errno = EINVAL;
993765cebfSchristos return NULL;
1003765cebfSchristos }
101b2459dacSchristos
1023765cebfSchristos good = false;
1033765cebfSchristos opentty = false;
1043765cebfSchristos if (fd == NULL) {
1053765cebfSchristos /*
1063765cebfSchristos * Try to use /dev/tty if possible; otherwise read from stdin
1073765cebfSchristos * and write to stderr.
1083765cebfSchristos */
1093765cebfSchristos fd = fdc;
1109a513d96Schristos if ((fd[0] = fd[1] = fd[2] = open(_PATH_TTY,
1119a513d96Schristos O_RDWR | O_CLOEXEC)) == -1) {
1123765cebfSchristos fd[0] = STDIN_FILENO;
1133765cebfSchristos fd[1] = fd[2] = STDERR_FILENO;
1143765cebfSchristos } else
1153765cebfSchristos opentty = true;
1163765cebfSchristos }
1173765cebfSchristos
1183765cebfSchristos sig = 0;
119969c948dSchristos allocated = buf == NULL;
120969c948dSchristos if (tcgetattr(fd[0], >) == -1) {
12148c7acbcSchristos havetty = false;
122969c948dSchristos if (flags & GETPASS_NEED_TTY)
12348c7acbcSchristos goto out;
12448c7acbcSchristos memset(>, -1, sizeof(gt));
12548c7acbcSchristos } else
12648c7acbcSchristos havetty = true;
12748c7acbcSchristos
12848c7acbcSchristos if (havetty) {
12948c7acbcSchristos struct termios st = gt;
13048c7acbcSchristos
13148c7acbcSchristos st.c_lflag &= ~(ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL|ISIG|ICANON);
13248c7acbcSchristos st.c_cc[VMIN] = 1;
13348c7acbcSchristos st.c_cc[VTIME] = 0;
134969c948dSchristos if (tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &st) == -1)
13548c7acbcSchristos goto out;
1368bb17685Schristos }
13748c7acbcSchristos
13848c7acbcSchristos if (prompt != NULL) {
13948c7acbcSchristos size_t plen = strlen(prompt);
140969c948dSchristos (void)write(fd[1], prompt, plen);
141969c948dSchristos }
142969c948dSchristos
143969c948dSchristos if (allocated) {
144969c948dSchristos len = 1024;
145969c948dSchristos if ((buf = malloc(len)) == NULL)
146969c948dSchristos goto restore;
14761f28255Scgd }
14848c7acbcSchristos
14948c7acbcSchristos c = '\1';
15048c7acbcSchristos lnext = false;
15148c7acbcSchristos for (size_t l = 0; c != '\0'; ) {
15277cac556Schristos if (tout) {
15377cac556Schristos struct pollfd pfd;
15477cac556Schristos pfd.fd = fd[0];
15577cac556Schristos pfd.events = POLLIN|POLLRDNORM;
15677cac556Schristos pfd.revents = 0;
15777cac556Schristos switch (poll(&pfd, 1, tout * 1000)) {
15877cac556Schristos case 0:
159d6d7452bSchristos errno = ETIMEDOUT;
16077cac556Schristos /*FALLTHROUGH*/
16177cac556Schristos case -1:
16277cac556Schristos goto restore;
16377cac556Schristos default:
16477cac556Schristos break;
16577cac556Schristos }
16677cac556Schristos }
167969c948dSchristos if (read(fd[0], &c, 1) != 1)
16848c7acbcSchristos goto restore;
16948c7acbcSchristos
17077cac556Schristos #define beep() \
17177cac556Schristos do \
172969c948dSchristos if (flags & GETPASS_NO_BEEP) \
173969c948dSchristos (void)write(fd[2], "\a", 1); \
174969c948dSchristos while (/*CONSTCOND*/ 0)
175969c948dSchristos #define erase() (void)write(fd[1], "\b \b", 3)
176b7d29129Schristos /*
177b7d29129Schristos * We test for both _POSIX_VDISABLE and NUL here because _POSIX_VDISABLE
178b7d29129Schristos * propagation does not seem to be very consistent on multiple daemon hops
179b7d29129Schristos * between different OS's. Perhaps we should not even bother with
180b7d29129Schristos * _POSIX_VDISABLE and use ~0 and 0 directly.
181b7d29129Schristos */
182b7d29129Schristos #define C(a, b) ((gt.c_cc[(a)] == _POSIX_VDISABLE || gt.c_cc[(a)] == '\0') ? \
183b7d29129Schristos (b) : gt.c_cc[(a)])
18448c7acbcSchristos if (lnext) {
18548c7acbcSchristos lnext = false;
18648c7acbcSchristos goto add;
18761f28255Scgd }
18848c7acbcSchristos
18948c7acbcSchristos /* Ignored */
19048c7acbcSchristos if (c == C(VREPRINT, CTRL('r')) || c == C(VSTART, CTRL('q')) ||
19148c7acbcSchristos c == C(VSTOP, CTRL('s')) || c == C(VSTATUS, CTRL('t')) ||
19248c7acbcSchristos c == C(VDISCARD, CTRL('o')))
19348c7acbcSchristos continue;
19448c7acbcSchristos
19548c7acbcSchristos /* Literal next */
19648c7acbcSchristos if (c == C(VLNEXT, CTRL('v'))) {
19748c7acbcSchristos lnext = true;
19848c7acbcSchristos continue;
19961f28255Scgd }
20048c7acbcSchristos
20148c7acbcSchristos /* Line or word kill, treat as reset */
20248c7acbcSchristos if (c == C(VKILL, CTRL('u')) || c == C(VWERASE, CTRL('w'))) {
203311b90b9Schristos if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR)) {
204969c948dSchristos while (l--)
205969c948dSchristos erase();
206969c948dSchristos }
20748c7acbcSchristos l = 0;
20848c7acbcSchristos continue;
20948c7acbcSchristos }
21048c7acbcSchristos
21148c7acbcSchristos /* Character erase */
21248c7acbcSchristos if (c == C(VERASE, CTRL('h'))) {
21348c7acbcSchristos if (l == 0)
21448c7acbcSchristos beep();
215969c948dSchristos else {
21648c7acbcSchristos l--;
217311b90b9Schristos if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR))
218969c948dSchristos erase();
219969c948dSchristos }
22048c7acbcSchristos continue;
22148c7acbcSchristos }
22248c7acbcSchristos
22310fa0b64Schristos /* tty signal characters */
22410fa0b64Schristos if (c == C(VINTR, CTRL('c'))) {
22510fa0b64Schristos sig = SIGINT;
22610fa0b64Schristos goto out;
22710fa0b64Schristos }
22810fa0b64Schristos if (c == C(VQUIT, CTRL('\\'))) {
22910fa0b64Schristos sig = SIGQUIT;
23010fa0b64Schristos goto out;
23110fa0b64Schristos }
23210fa0b64Schristos if (c == C(VSUSP, CTRL('z')) || c == C(VDSUSP, CTRL('y'))) {
23310fa0b64Schristos sig = SIGTSTP;
23448c7acbcSchristos goto out;
23548c7acbcSchristos }
23648c7acbcSchristos
23710fa0b64Schristos /* EOF */
23810fa0b64Schristos if (c == C(VEOF, CTRL('d'))) {
239969c948dSchristos if (flags & GETPASS_FAIL_EOF) {
24010fa0b64Schristos errno = ENODATA;
24110fa0b64Schristos goto out;
242969c948dSchristos } else {
24310fa0b64Schristos c = '\0';
24410fa0b64Schristos goto add;
245969c948dSchristos }
24610fa0b64Schristos }
24710fa0b64Schristos
24810fa0b64Schristos /* End of line */
2491913c941Schristos if (c == C(VEOL, CTRL('j')) || c == C(VEOL2, CTRL('m')))
25048c7acbcSchristos c = '\0';
25148c7acbcSchristos add:
25248c7acbcSchristos if (l >= len) {
253969c948dSchristos if (allocated) {
25498ba7d20Schristos size_t nlen = len + 1024;
25598ba7d20Schristos char *nbuf = realloc(buf, nlen);
25698ba7d20Schristos if (nbuf == NULL)
257969c948dSchristos goto restore;
25898ba7d20Schristos buf = nbuf;
25998ba7d20Schristos len = nlen;
260969c948dSchristos } else {
261969c948dSchristos if (flags & GETPASS_BUF_LIMIT) {
26248c7acbcSchristos beep();
26348c7acbcSchristos continue;
264969c948dSchristos }
26548c7acbcSchristos if (c == '\0' && l > 0)
26648c7acbcSchristos l--;
26748c7acbcSchristos else
26848c7acbcSchristos continue;
26948c7acbcSchristos }
270969c948dSchristos }
2713765cebfSchristos
2723765cebfSchristos if (flags & GETPASS_7BIT)
2733765cebfSchristos c &= 0x7f;
2743765cebfSchristos if ((flags & GETPASS_FORCE_LOWER) && isupper((unsigned char)c))
2753765cebfSchristos c = tolower((unsigned char)c);
2763765cebfSchristos if ((flags & GETPASS_FORCE_UPPER) && islower((unsigned char)c))
2773765cebfSchristos c = toupper((unsigned char)c);
2783765cebfSchristos
279969c948dSchristos buf[l++] = c;
280311b90b9Schristos if (c) {
281311b90b9Schristos if (flags & GETPASS_ECHO_STAR)
282311b90b9Schristos (void)write(fd[1], "*", 1);
283311b90b9Schristos else if (flags & GETPASS_ECHO)
28498ba7d20Schristos (void)write(fd[1], isprint((unsigned char)c) ?
28598ba7d20Schristos &c : "?", 1);
28648c7acbcSchristos }
287311b90b9Schristos }
2883765cebfSchristos good = true;
28948c7acbcSchristos
29048c7acbcSchristos restore:
291*8397619cSchristos out:
292969c948dSchristos if (havetty) {
29348c7acbcSchristos c = errno;
294969c948dSchristos (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, >);
29548c7acbcSchristos errno = c;
296969c948dSchristos }
2973765cebfSchristos if (good && (flags & GETPASS_ECHO_NL))
2983765cebfSchristos (void)write(fd[1], "\n", 1);
2993765cebfSchristos
3003765cebfSchristos if (opentty) {
3013765cebfSchristos c = errno;
3023765cebfSchristos (void)close(fd[0]);
3033765cebfSchristos errno = c;
3043765cebfSchristos }
3053765cebfSchristos
3063765cebfSchristos if (good)
3073765cebfSchristos return buf;
3083765cebfSchristos
30910fa0b64Schristos if (sig) {
310969c948dSchristos if ((flags & GETPASS_NO_SIGNAL) == 0)
31110fa0b64Schristos (void)raise(sig);
31210fa0b64Schristos errno = EINTR;
31310fa0b64Schristos }
314969c948dSchristos memset(buf, 0, len);
315969c948dSchristos if (allocated)
316969c948dSchristos free(buf);
31748c7acbcSchristos return NULL;
31848c7acbcSchristos }
31948c7acbcSchristos
32048c7acbcSchristos char *
getpass_r(const char * prompt,char * buf,size_t len)321969c948dSchristos getpass_r(const char *prompt, char *buf, size_t len)
322969c948dSchristos {
3238f44324eSchristos return getpassfd(prompt, buf, len, NULL, GETPASS_ECHO_NL, 0);
324969c948dSchristos }
325969c948dSchristos
326969c948dSchristos char *
getpass(const char * prompt)32748c7acbcSchristos getpass(const char *prompt)
32848c7acbcSchristos {
32948c7acbcSchristos static char e[] = "";
33048c7acbcSchristos static char *buf;
33148c7acbcSchristos static long bufsiz;
33248c7acbcSchristos char *rv;
33348c7acbcSchristos
334969c948dSchristos /*
335969c948dSchristos * Strictly speaking we could double allocate here, if we get
336969c948dSchristos * called at the same time, but this function is not re-entrant
337969c948dSchristos * anyway and it is not supposed to work if called concurrently.
338969c948dSchristos */
33948c7acbcSchristos if (buf == NULL) {
34048c7acbcSchristos if ((bufsiz = sysconf(_SC_PASS_MAX)) == -1)
34148c7acbcSchristos return e;
34248c7acbcSchristos if ((buf = malloc((size_t)bufsiz)) == NULL)
34348c7acbcSchristos return e;
34448c7acbcSchristos }
34548c7acbcSchristos
34648c7acbcSchristos if ((rv = getpass_r(prompt, buf, (size_t)bufsiz)) == NULL)
34748c7acbcSchristos return e;
34848c7acbcSchristos
34948c7acbcSchristos return rv;
35048c7acbcSchristos }
35148c7acbcSchristos
35248c7acbcSchristos #ifdef TEST
35348c7acbcSchristos int
main(int argc,char * argv[])35448c7acbcSchristos main(int argc, char *argv[])
35548c7acbcSchristos {
35648c7acbcSchristos char buf[28];
3573765cebfSchristos printf("[%s]\n", getpassfd("foo>", buf, sizeof(buf), NULL,
3583765cebfSchristos GETPASS_ECHO_STAR|GETPASS_ECHO_NL, 2));
35948c7acbcSchristos return 0;
36048c7acbcSchristos }
36148c7acbcSchristos #endif
362