xref: /freebsd/lib/libc/gen/readpassphrase.c (revision f29af3b2ac59fb14df3190a056256131a0190c0f)
1*f29af3b2SXin LI /*	$OpenBSD: readpassphrase.c,v 1.23 2010/05/14 13:30:34 millert Exp $	*/
2a7a8a766SBrian Feldman 
313d98e8cSBrian Feldman /*
4*f29af3b2SXin LI  * Copyright (c) 2000-2002, 2007, 2010
5*f29af3b2SXin LI  *	Todd C. Miller <Todd.Miller@courtesan.com>
613d98e8cSBrian Feldman  *
7*f29af3b2SXin LI  * Permission to use, copy, modify, and distribute this software for any
8*f29af3b2SXin LI  * purpose with or without fee is hereby granted, provided that the above
9*f29af3b2SXin LI  * copyright notice and this permission notice appear in all copies.
1013d98e8cSBrian Feldman  *
11*f29af3b2SXin LI  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12*f29af3b2SXin LI  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13*f29af3b2SXin LI  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14*f29af3b2SXin LI  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15*f29af3b2SXin LI  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16*f29af3b2SXin LI  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17*f29af3b2SXin LI  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*f29af3b2SXin LI  *
19*f29af3b2SXin LI  * Sponsored in part by the Defense Advanced Research Projects
20*f29af3b2SXin LI  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21*f29af3b2SXin LI  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
2213d98e8cSBrian Feldman  */
2313d98e8cSBrian Feldman 
241e450813SMax Khon #include <sys/cdefs.h>
2513d98e8cSBrian Feldman __FBSDID("$FreeBSD$");
2613d98e8cSBrian Feldman 
2770d2a9e1SBrian Feldman #include "namespace.h"
2813d98e8cSBrian Feldman #include <ctype.h>
2913d98e8cSBrian Feldman #include <errno.h>
3013d98e8cSBrian Feldman #include <fcntl.h>
3113d98e8cSBrian Feldman #include <paths.h>
3213d98e8cSBrian Feldman #include <pwd.h>
3313d98e8cSBrian Feldman #include <signal.h>
3413d98e8cSBrian Feldman #include <string.h>
3513d98e8cSBrian Feldman #include <termios.h>
3613d98e8cSBrian Feldman #include <unistd.h>
3713d98e8cSBrian Feldman #include <readpassphrase.h>
3870d2a9e1SBrian Feldman #include "un-namespace.h"
3913d98e8cSBrian Feldman 
40*f29af3b2SXin LI static volatile sig_atomic_t signo[NSIG];
41a7a8a766SBrian Feldman 
42a7a8a766SBrian Feldman static void handler(int);
43a7a8a766SBrian Feldman 
4413d98e8cSBrian Feldman char *
45a7a8a766SBrian Feldman readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
4613d98e8cSBrian Feldman {
47a7a8a766SBrian Feldman 	ssize_t nr;
48*f29af3b2SXin LI 	int input, output, save_errno, i, need_restart;
4913d98e8cSBrian Feldman 	char ch, *p, *end;
50a7a8a766SBrian Feldman 	struct termios term, oterm;
51*f29af3b2SXin LI 	struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
52*f29af3b2SXin LI 	struct sigaction savetstp, savettin, savettou, savepipe;
5313d98e8cSBrian Feldman 
5413d98e8cSBrian Feldman 	/* I suppose we could alloc on demand in this case (XXX). */
5513d98e8cSBrian Feldman 	if (bufsiz == 0) {
5613d98e8cSBrian Feldman 		errno = EINVAL;
5713d98e8cSBrian Feldman 		return(NULL);
5813d98e8cSBrian Feldman 	}
5913d98e8cSBrian Feldman 
60a7a8a766SBrian Feldman restart:
61*f29af3b2SXin LI 	for (i = 0; i < NSIG; i++)
62*f29af3b2SXin LI 		signo[i] = 0;
63*f29af3b2SXin LI 	nr = -1;
64*f29af3b2SXin LI 	save_errno = 0;
65*f29af3b2SXin LI 	need_restart = 0;
6613d98e8cSBrian Feldman 	/*
6713d98e8cSBrian Feldman 	 * Read and write to /dev/tty if available.  If not, read from
6813d98e8cSBrian Feldman 	 * stdin and write to stderr unless a tty is required.
6913d98e8cSBrian Feldman 	 */
70*f29af3b2SXin LI 	if ((flags & RPP_STDIN) ||
71*f29af3b2SXin LI 	    (input = output = _open(_PATH_TTY, O_RDWR)) == -1) {
7213d98e8cSBrian Feldman 		if (flags & RPP_REQUIRE_TTY) {
7313d98e8cSBrian Feldman 			errno = ENOTTY;
7413d98e8cSBrian Feldman 			return(NULL);
7513d98e8cSBrian Feldman 		}
7613d98e8cSBrian Feldman 		input = STDIN_FILENO;
7713d98e8cSBrian Feldman 		output = STDERR_FILENO;
7813d98e8cSBrian Feldman 	}
7913d98e8cSBrian Feldman 
8013d98e8cSBrian Feldman 	/*
81*f29af3b2SXin LI 	 * Turn off echo if possible.
82*f29af3b2SXin LI 	 * If we are using a tty but are not the foreground pgrp this will
83*f29af3b2SXin LI 	 * generate SIGTTOU, so do it *before* installing the signal handlers.
8413d98e8cSBrian Feldman 	 */
85*f29af3b2SXin LI 	if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
8613d98e8cSBrian Feldman 		memcpy(&term, &oterm, sizeof(term));
87a7a8a766SBrian Feldman 		if (!(flags & RPP_ECHO_ON))
88a7a8a766SBrian Feldman 			term.c_lflag &= ~(ECHO | ECHONL);
8913d98e8cSBrian Feldman 		if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
9013d98e8cSBrian Feldman 			term.c_cc[VSTATUS] = _POSIX_VDISABLE;
9113d98e8cSBrian Feldman 		(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
9213d98e8cSBrian Feldman 	} else {
9313d98e8cSBrian Feldman 		memset(&term, 0, sizeof(term));
94*f29af3b2SXin LI 		term.c_lflag |= ECHO;
9513d98e8cSBrian Feldman 		memset(&oterm, 0, sizeof(oterm));
96*f29af3b2SXin LI 		oterm.c_lflag |= ECHO;
9713d98e8cSBrian Feldman 	}
9813d98e8cSBrian Feldman 
99*f29af3b2SXin LI 	/*
100*f29af3b2SXin LI 	 * Catch signals that would otherwise cause the user to end
101*f29af3b2SXin LI 	 * up with echo turned off in the shell.  Don't worry about
102*f29af3b2SXin LI 	 * things like SIGXCPU and SIGVTALRM for now.
103*f29af3b2SXin LI 	 */
104*f29af3b2SXin LI 	sigemptyset(&sa.sa_mask);
105*f29af3b2SXin LI 	sa.sa_flags = 0;		/* don't restart system calls */
106*f29af3b2SXin LI 	sa.sa_handler = handler;
107*f29af3b2SXin LI 	(void)_sigaction(SIGALRM, &sa, &savealrm);
108*f29af3b2SXin LI 	(void)_sigaction(SIGHUP, &sa, &savehup);
109*f29af3b2SXin LI 	(void)_sigaction(SIGINT, &sa, &saveint);
110*f29af3b2SXin LI 	(void)_sigaction(SIGPIPE, &sa, &savepipe);
111*f29af3b2SXin LI 	(void)_sigaction(SIGQUIT, &sa, &savequit);
112*f29af3b2SXin LI 	(void)_sigaction(SIGTERM, &sa, &saveterm);
113*f29af3b2SXin LI 	(void)_sigaction(SIGTSTP, &sa, &savetstp);
114*f29af3b2SXin LI 	(void)_sigaction(SIGTTIN, &sa, &savettin);
115*f29af3b2SXin LI 	(void)_sigaction(SIGTTOU, &sa, &savettou);
116*f29af3b2SXin LI 
117*f29af3b2SXin LI 	if (!(flags & RPP_STDIN))
118a939ced8SBrian Feldman 		(void)_write(output, prompt, strlen(prompt));
11913d98e8cSBrian Feldman 	end = buf + bufsiz - 1;
120*f29af3b2SXin LI 	p = buf;
121*f29af3b2SXin LI 	while ((nr = _read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
12213d98e8cSBrian Feldman 		if (p < end) {
12313d98e8cSBrian Feldman 			if ((flags & RPP_SEVENBIT))
12413d98e8cSBrian Feldman 				ch &= 0x7f;
12513d98e8cSBrian Feldman 			if (isalpha(ch)) {
12613d98e8cSBrian Feldman 				if ((flags & RPP_FORCELOWER))
127*f29af3b2SXin LI 					ch = (char)tolower(ch);
12813d98e8cSBrian Feldman 				if ((flags & RPP_FORCEUPPER))
129*f29af3b2SXin LI 					ch = (char)toupper(ch);
13013d98e8cSBrian Feldman 			}
13113d98e8cSBrian Feldman 			*p++ = ch;
13213d98e8cSBrian Feldman 		}
13313d98e8cSBrian Feldman 	}
13413d98e8cSBrian Feldman 	*p = '\0';
135a7a8a766SBrian Feldman 	save_errno = errno;
13613d98e8cSBrian Feldman 	if (!(term.c_lflag & ECHO))
137a939ced8SBrian Feldman 		(void)_write(output, "\n", 1);
13813d98e8cSBrian Feldman 
139a7a8a766SBrian Feldman 	/* Restore old terminal settings and signals. */
140*f29af3b2SXin LI 	if (memcmp(&term, &oterm, sizeof(term)) != 0) {
141*f29af3b2SXin LI 		while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 &&
142*f29af3b2SXin LI 		    errno == EINTR && !signo[SIGTTOU])
143*f29af3b2SXin LI 			continue;
144*f29af3b2SXin LI 	}
145*f29af3b2SXin LI 	(void)_sigaction(SIGALRM, &savealrm, NULL);
146a7a8a766SBrian Feldman 	(void)_sigaction(SIGHUP, &savehup, NULL);
147*f29af3b2SXin LI 	(void)_sigaction(SIGINT, &saveint, NULL);
148a7a8a766SBrian Feldman 	(void)_sigaction(SIGQUIT, &savequit, NULL);
149*f29af3b2SXin LI 	(void)_sigaction(SIGPIPE, &savepipe, NULL);
150a7a8a766SBrian Feldman 	(void)_sigaction(SIGTERM, &saveterm, NULL);
151a7a8a766SBrian Feldman 	(void)_sigaction(SIGTSTP, &savetstp, NULL);
152a7a8a766SBrian Feldman 	(void)_sigaction(SIGTTIN, &savettin, NULL);
153a7a8a766SBrian Feldman 	(void)_sigaction(SIGTTOU, &savettou, NULL);
15413d98e8cSBrian Feldman 	if (input != STDIN_FILENO)
155a939ced8SBrian Feldman 		(void)_close(input);
156a7a8a766SBrian Feldman 
157a7a8a766SBrian Feldman 	/*
158a7a8a766SBrian Feldman 	 * If we were interrupted by a signal, resend it to ourselves
159a7a8a766SBrian Feldman 	 * now that we have restored the signal handlers.
160a7a8a766SBrian Feldman 	 */
161*f29af3b2SXin LI 	for (i = 0; i < NSIG; i++) {
162*f29af3b2SXin LI 		if (signo[i]) {
163*f29af3b2SXin LI 			kill(getpid(), i);
164*f29af3b2SXin LI 			switch (i) {
165a7a8a766SBrian Feldman 			case SIGTSTP:
166a7a8a766SBrian Feldman 			case SIGTTIN:
167a7a8a766SBrian Feldman 			case SIGTTOU:
168*f29af3b2SXin LI 				need_restart = 1;
169*f29af3b2SXin LI 			}
170*f29af3b2SXin LI 		}
171*f29af3b2SXin LI 	}
172*f29af3b2SXin LI 	if (need_restart)
173a7a8a766SBrian Feldman 		goto restart;
17413d98e8cSBrian Feldman 
175*f29af3b2SXin LI 	if (save_errno)
176a7a8a766SBrian Feldman 		errno = save_errno;
177a7a8a766SBrian Feldman 	return(nr == -1 ? NULL : buf);
178a7a8a766SBrian Feldman }
179a7a8a766SBrian Feldman 
18013d98e8cSBrian Feldman char *
181a7a8a766SBrian Feldman getpass(const char *prompt)
18213d98e8cSBrian Feldman {
18313d98e8cSBrian Feldman 	static char buf[_PASSWORD_LEN + 1];
18413d98e8cSBrian Feldman 
18544f7d6afSBrian Feldman 	if (readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF) == NULL)
18644f7d6afSBrian Feldman 		buf[0] = '\0';
18744f7d6afSBrian Feldman 	return(buf);
18813d98e8cSBrian Feldman }
189a7a8a766SBrian Feldman 
190a7a8a766SBrian Feldman static void handler(int s)
191a7a8a766SBrian Feldman {
192a7a8a766SBrian Feldman 
193*f29af3b2SXin LI 	signo[s] = 1;
194a7a8a766SBrian Feldman }
195