readpassphrase.c revision 86733
1/*
2 * Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 *    derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
19 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#if defined(LIBC_SCCS) && !defined(lint)
29static const char rcsid[] = "$OpenBSD: readpassphrase.c,v 1.7 2001/08/07 19:34:11 millert Exp $";
30#endif /* LIBC_SCCS and not lint */
31include <sys/cdefs.h>
32__FBSDID("$FreeBSD: head/lib/libc/gen/readpassphrase.c 86733 2001-11-21 15:33:40Z green $");
33
34#include "namespace.h"
35#include <ctype.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <paths.h>
39#include <pwd.h>
40#include <signal.h>
41#include <string.h>
42#include <termios.h>
43#include <unistd.h>
44#include <readpassphrase.h>
45#include "un-namespace.h"
46
47char *
48readpassphrase(prompt, buf, bufsiz, flags)
49	const char *prompt;
50	char *buf;
51	size_t bufsiz;
52	int flags;
53{
54	struct termios term, oterm;
55	char ch, *p, *end;
56	int input, output;
57	sigset_t oset, nset;
58
59	/* I suppose we could alloc on demand in this case (XXX). */
60	if (bufsiz == 0) {
61		errno = EINVAL;
62		return(NULL);
63	}
64
65	/*
66	 * Read and write to /dev/tty if available.  If not, read from
67	 * stdin and write to stderr unless a tty is required.
68	 */
69	if ((input = output = _open(_PATH_TTY, O_RDWR)) == -1) {
70		if (flags & RPP_REQUIRE_TTY) {
71			errno = ENOTTY;
72			return(NULL);
73		}
74		input = STDIN_FILENO;
75		output = STDERR_FILENO;
76	}
77
78	/*
79	 * We block SIGINT and SIGTSTP so the terminal is not left
80	 * in an inconsistent state (ie: no echo).  It would probably
81	 * be better to simply catch these though.
82	 */
83	sigemptyset(&nset);
84	sigaddset(&nset, SIGINT);
85	sigaddset(&nset, SIGTSTP);
86	(void)_sigprocmask(SIG_BLOCK, &nset, &oset);
87
88	/* Turn off echo if possible. */
89	if (tcgetattr(input, &oterm) == 0) {
90		memcpy(&term, &oterm, sizeof(term));
91		if (!(flags & RPP_ECHO_ON) && (term.c_lflag & ECHO))
92			term.c_lflag &= ~ECHO;
93		if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
94			term.c_cc[VSTATUS] = _POSIX_VDISABLE;
95		(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
96	} else {
97		memset(&term, 0, sizeof(term));
98		memset(&oterm, 0, sizeof(oterm));
99	}
100
101	(void)_write(output, prompt, strlen(prompt));
102	end = buf + bufsiz - 1;
103	for (p = buf; _read(input, &ch, 1) == 1 && ch != '\n' && ch != '\r';) {
104		if (p < end) {
105			if ((flags & RPP_SEVENBIT))
106				ch &= 0x7f;
107			if (isalpha(ch)) {
108				if ((flags & RPP_FORCELOWER))
109					ch = tolower(ch);
110				if ((flags & RPP_FORCEUPPER))
111					ch = toupper(ch);
112			}
113			*p++ = ch;
114		}
115	}
116	*p = '\0';
117	if (!(term.c_lflag & ECHO))
118		(void)_write(output, "\n", 1);
119
120	/* Restore old terminal settings and signal mask. */
121	if (memcmp(&term, &oterm, sizeof(term)) != 0)
122		(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm);
123	(void)_sigprocmask(SIG_SETMASK, &oset, NULL);
124	if (input != STDIN_FILENO)
125		(void)_close(input);
126	return(buf);
127}
128
129char *
130getpass(prompt)
131        const char *prompt;
132{
133	static char buf[_PASSWORD_LEN + 1];
134
135	return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
136}
137