readpassphrase.c revision 86669
186669Sgreen/*
286669Sgreen * Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com>
386669Sgreen * All rights reserved.
486669Sgreen *
586669Sgreen * Redistribution and use in source and binary forms, with or without
686669Sgreen * modification, are permitted provided that the following conditions
786669Sgreen * are met:
886669Sgreen * 1. Redistributions of source code must retain the above copyright
986669Sgreen *    notice, this list of conditions and the following disclaimer.
1086669Sgreen * 2. Redistributions in binary form must reproduce the above copyright
1186669Sgreen *    notice, this list of conditions and the following disclaimer in the
1286669Sgreen *    documentation and/or other materials provided with the distribution.
1386669Sgreen * 3. The name of the author may not be used to endorse or promote products
1486669Sgreen *    derived from this software without specific prior written permission.
1586669Sgreen *
1686669Sgreen * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
1786669Sgreen * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
1886669Sgreen * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
1986669Sgreen * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2086669Sgreen * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2186669Sgreen * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2286669Sgreen * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2386669Sgreen * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2486669Sgreen * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2586669Sgreen * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2686669Sgreen */
2786669Sgreen
2886669Sgreen#if defined(LIBC_SCCS) && !defined(lint)
2986669Sgreenstatic const char rcsid[] = "$OpenBSD: readpassphrase.c,v 1.7 2001/08/07 19:34:11 millert Exp $";
3086669Sgreen#endif /* LIBC_SCCS and not lint */
3186669Sgreen#include <sys/cdefs.h>
3286669Sgreen__FBSDID("$FreeBSD: head/lib/libc/gen/readpassphrase.c 86669 2001-11-20 15:15:42Z green $");
3386669Sgreen
3486669Sgreen#include <ctype.h>
3586669Sgreen#include <errno.h>
3686669Sgreen#include <fcntl.h>
3786669Sgreen#include <paths.h>
3886669Sgreen#include <pwd.h>
3986669Sgreen#include <signal.h>
4086669Sgreen#include <string.h>
4186669Sgreen#include <termios.h>
4286669Sgreen#include <unistd.h>
4386669Sgreen#include <readpassphrase.h>
4486669Sgreen
4586669Sgreenchar *
4686669Sgreenreadpassphrase(prompt, buf, bufsiz, flags)
4786669Sgreen	const char *prompt;
4886669Sgreen	char *buf;
4986669Sgreen	size_t bufsiz;
5086669Sgreen	int flags;
5186669Sgreen{
5286669Sgreen	struct termios term, oterm;
5386669Sgreen	char ch, *p, *end;
5486669Sgreen	int input, output;
5586669Sgreen	sigset_t oset, nset;
5686669Sgreen
5786669Sgreen	/* I suppose we could alloc on demand in this case (XXX). */
5886669Sgreen	if (bufsiz == 0) {
5986669Sgreen		errno = EINVAL;
6086669Sgreen		return(NULL);
6186669Sgreen	}
6286669Sgreen
6386669Sgreen	/*
6486669Sgreen	 * Read and write to /dev/tty if available.  If not, read from
6586669Sgreen	 * stdin and write to stderr unless a tty is required.
6686669Sgreen	 */
6786669Sgreen	if ((input = output = open(_PATH_TTY, O_RDWR)) == -1) {
6886669Sgreen		if (flags & RPP_REQUIRE_TTY) {
6986669Sgreen			errno = ENOTTY;
7086669Sgreen			return(NULL);
7186669Sgreen		}
7286669Sgreen		input = STDIN_FILENO;
7386669Sgreen		output = STDERR_FILENO;
7486669Sgreen	}
7586669Sgreen
7686669Sgreen	/*
7786669Sgreen	 * We block SIGINT and SIGTSTP so the terminal is not left
7886669Sgreen	 * in an inconsistent state (ie: no echo).  It would probably
7986669Sgreen	 * be better to simply catch these though.
8086669Sgreen	 */
8186669Sgreen	sigemptyset(&nset);
8286669Sgreen	sigaddset(&nset, SIGINT);
8386669Sgreen	sigaddset(&nset, SIGTSTP);
8486669Sgreen	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
8586669Sgreen
8686669Sgreen	/* Turn off echo if possible. */
8786669Sgreen	if (tcgetattr(input, &oterm) == 0) {
8886669Sgreen		memcpy(&term, &oterm, sizeof(term));
8986669Sgreen		if (!(flags & RPP_ECHO_ON) && (term.c_lflag & ECHO))
9086669Sgreen			term.c_lflag &= ~ECHO;
9186669Sgreen		if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
9286669Sgreen			term.c_cc[VSTATUS] = _POSIX_VDISABLE;
9386669Sgreen		(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
9486669Sgreen	} else {
9586669Sgreen		memset(&term, 0, sizeof(term));
9686669Sgreen		memset(&oterm, 0, sizeof(oterm));
9786669Sgreen	}
9886669Sgreen
9986669Sgreen	(void)write(output, prompt, strlen(prompt));
10086669Sgreen	end = buf + bufsiz - 1;
10186669Sgreen	for (p = buf; read(input, &ch, 1) == 1 && ch != '\n' && ch != '\r';) {
10286669Sgreen		if (p < end) {
10386669Sgreen			if ((flags & RPP_SEVENBIT))
10486669Sgreen				ch &= 0x7f;
10586669Sgreen			if (isalpha(ch)) {
10686669Sgreen				if ((flags & RPP_FORCELOWER))
10786669Sgreen					ch = tolower(ch);
10886669Sgreen				if ((flags & RPP_FORCEUPPER))
10986669Sgreen					ch = toupper(ch);
11086669Sgreen			}
11186669Sgreen			*p++ = ch;
11286669Sgreen		}
11386669Sgreen	}
11486669Sgreen	*p = '\0';
11586669Sgreen	if (!(term.c_lflag & ECHO))
11686669Sgreen		(void)write(output, "\n", 1);
11786669Sgreen
11886669Sgreen	/* Restore old terminal settings and signal mask. */
11986669Sgreen	if (memcmp(&term, &oterm, sizeof(term)) != 0)
12086669Sgreen		(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm);
12186669Sgreen	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
12286669Sgreen	if (input != STDIN_FILENO)
12386669Sgreen		(void)close(input);
12486669Sgreen	return(buf);
12586669Sgreen}
12686669Sgreen
12786669Sgreenchar *
12886669Sgreengetpass(prompt)
12986669Sgreen        const char *prompt;
13086669Sgreen{
13186669Sgreen	static char buf[_PASSWORD_LEN + 1];
13286669Sgreen
13386669Sgreen	return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
13486669Sgreen}
135