readpassphrase.c revision 86750
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 */
3186750Sfjoe#include <sys/cdefs.h>
3286669Sgreen__FBSDID("$FreeBSD: head/lib/libc/gen/readpassphrase.c 86750 2001-11-21 20:40:21Z fjoe $");
3386669Sgreen
3486733Sgreen#include "namespace.h"
3586669Sgreen#include <ctype.h>
3686669Sgreen#include <errno.h>
3786669Sgreen#include <fcntl.h>
3886669Sgreen#include <paths.h>
3986669Sgreen#include <pwd.h>
4086669Sgreen#include <signal.h>
4186669Sgreen#include <string.h>
4286669Sgreen#include <termios.h>
4386669Sgreen#include <unistd.h>
4486669Sgreen#include <readpassphrase.h>
4586733Sgreen#include "un-namespace.h"
4686669Sgreen
4786669Sgreenchar *
4886669Sgreenreadpassphrase(prompt, buf, bufsiz, flags)
4986669Sgreen	const char *prompt;
5086669Sgreen	char *buf;
5186669Sgreen	size_t bufsiz;
5286669Sgreen	int flags;
5386669Sgreen{
5486669Sgreen	struct termios term, oterm;
5586669Sgreen	char ch, *p, *end;
5686669Sgreen	int input, output;
5786669Sgreen	sigset_t oset, nset;
5886669Sgreen
5986669Sgreen	/* I suppose we could alloc on demand in this case (XXX). */
6086669Sgreen	if (bufsiz == 0) {
6186669Sgreen		errno = EINVAL;
6286669Sgreen		return(NULL);
6386669Sgreen	}
6486669Sgreen
6586669Sgreen	/*
6686669Sgreen	 * Read and write to /dev/tty if available.  If not, read from
6786669Sgreen	 * stdin and write to stderr unless a tty is required.
6886669Sgreen	 */
6986732Sgreen	if ((input = output = _open(_PATH_TTY, O_RDWR)) == -1) {
7086669Sgreen		if (flags & RPP_REQUIRE_TTY) {
7186669Sgreen			errno = ENOTTY;
7286669Sgreen			return(NULL);
7386669Sgreen		}
7486669Sgreen		input = STDIN_FILENO;
7586669Sgreen		output = STDERR_FILENO;
7686669Sgreen	}
7786669Sgreen
7886669Sgreen	/*
7986669Sgreen	 * We block SIGINT and SIGTSTP so the terminal is not left
8086669Sgreen	 * in an inconsistent state (ie: no echo).  It would probably
8186669Sgreen	 * be better to simply catch these though.
8286669Sgreen	 */
8386669Sgreen	sigemptyset(&nset);
8486669Sgreen	sigaddset(&nset, SIGINT);
8586669Sgreen	sigaddset(&nset, SIGTSTP);
8686732Sgreen	(void)_sigprocmask(SIG_BLOCK, &nset, &oset);
8786669Sgreen
8886669Sgreen	/* Turn off echo if possible. */
8986669Sgreen	if (tcgetattr(input, &oterm) == 0) {
9086669Sgreen		memcpy(&term, &oterm, sizeof(term));
9186669Sgreen		if (!(flags & RPP_ECHO_ON) && (term.c_lflag & ECHO))
9286669Sgreen			term.c_lflag &= ~ECHO;
9386669Sgreen		if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
9486669Sgreen			term.c_cc[VSTATUS] = _POSIX_VDISABLE;
9586669Sgreen		(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
9686669Sgreen	} else {
9786669Sgreen		memset(&term, 0, sizeof(term));
9886669Sgreen		memset(&oterm, 0, sizeof(oterm));
9986669Sgreen	}
10086669Sgreen
10186732Sgreen	(void)_write(output, prompt, strlen(prompt));
10286669Sgreen	end = buf + bufsiz - 1;
10386732Sgreen	for (p = buf; _read(input, &ch, 1) == 1 && ch != '\n' && ch != '\r';) {
10486669Sgreen		if (p < end) {
10586669Sgreen			if ((flags & RPP_SEVENBIT))
10686669Sgreen				ch &= 0x7f;
10786669Sgreen			if (isalpha(ch)) {
10886669Sgreen				if ((flags & RPP_FORCELOWER))
10986669Sgreen					ch = tolower(ch);
11086669Sgreen				if ((flags & RPP_FORCEUPPER))
11186669Sgreen					ch = toupper(ch);
11286669Sgreen			}
11386669Sgreen			*p++ = ch;
11486669Sgreen		}
11586669Sgreen	}
11686669Sgreen	*p = '\0';
11786669Sgreen	if (!(term.c_lflag & ECHO))
11886732Sgreen		(void)_write(output, "\n", 1);
11986669Sgreen
12086669Sgreen	/* Restore old terminal settings and signal mask. */
12186669Sgreen	if (memcmp(&term, &oterm, sizeof(term)) != 0)
12286669Sgreen		(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm);
12386732Sgreen	(void)_sigprocmask(SIG_SETMASK, &oset, NULL);
12486669Sgreen	if (input != STDIN_FILENO)
12586732Sgreen		(void)_close(input);
12686669Sgreen	return(buf);
12786669Sgreen}
12886669Sgreen
12986669Sgreenchar *
13086669Sgreengetpass(prompt)
13186669Sgreen        const char *prompt;
13286669Sgreen{
13386669Sgreen	static char buf[_PASSWORD_LEN + 1];
13486669Sgreen
13586669Sgreen	return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
13686669Sgreen}
137