xref: /freebsd/contrib/libfido2/openbsd-compat/readpassphrase.c (revision 0afa8e065e14bb8fd338d75690e0238c00167d40)
1*0afa8e06SEd Maste /*	$OpenBSD: readpassphrase.c,v 1.26 2016/10/18 12:47:18 millert Exp $	*/
2*0afa8e06SEd Maste 
3*0afa8e06SEd Maste /*
4*0afa8e06SEd Maste  * Copyright (c) 2000-2002, 2007, 2010
5*0afa8e06SEd Maste  *	Todd C. Miller <Todd.Miller@courtesan.com>
6*0afa8e06SEd Maste  *
7*0afa8e06SEd Maste  * Permission to use, copy, modify, and distribute this software for any
8*0afa8e06SEd Maste  * purpose with or without fee is hereby granted, provided that the above
9*0afa8e06SEd Maste  * copyright notice and this permission notice appear in all copies.
10*0afa8e06SEd Maste  *
11*0afa8e06SEd Maste  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12*0afa8e06SEd Maste  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13*0afa8e06SEd Maste  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14*0afa8e06SEd Maste  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15*0afa8e06SEd Maste  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16*0afa8e06SEd Maste  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17*0afa8e06SEd Maste  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*0afa8e06SEd Maste  *
19*0afa8e06SEd Maste  * Sponsored in part by the Defense Advanced Research Projects
20*0afa8e06SEd Maste  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21*0afa8e06SEd Maste  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22*0afa8e06SEd Maste  */
23*0afa8e06SEd Maste 
24*0afa8e06SEd Maste /* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */
25*0afa8e06SEd Maste 
26*0afa8e06SEd Maste #include "openbsd-compat.h"
27*0afa8e06SEd Maste 
28*0afa8e06SEd Maste #ifndef HAVE_READPASSPHRASE
29*0afa8e06SEd Maste 
30*0afa8e06SEd Maste #include <termios.h>
31*0afa8e06SEd Maste #include <signal.h>
32*0afa8e06SEd Maste #include <ctype.h>
33*0afa8e06SEd Maste #include <fcntl.h>
34*0afa8e06SEd Maste #include <errno.h>
35*0afa8e06SEd Maste #include <string.h>
36*0afa8e06SEd Maste #ifdef HAVE_UNISTD_H
37*0afa8e06SEd Maste #include <unistd.h>
38*0afa8e06SEd Maste #endif
39*0afa8e06SEd Maste #include <paths.h>
40*0afa8e06SEd Maste 
41*0afa8e06SEd Maste #ifndef _PATH_TTY
42*0afa8e06SEd Maste # define _PATH_TTY "/dev/tty"
43*0afa8e06SEd Maste #endif
44*0afa8e06SEd Maste 
45*0afa8e06SEd Maste #ifndef TCSASOFT
46*0afa8e06SEd Maste /* If we don't have TCSASOFT define it so that ORing it it below is a no-op. */
47*0afa8e06SEd Maste # define TCSASOFT 0
48*0afa8e06SEd Maste #endif
49*0afa8e06SEd Maste 
50*0afa8e06SEd Maste /* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */
51*0afa8e06SEd Maste #if !defined(_POSIX_VDISABLE) && defined(VDISABLE)
52*0afa8e06SEd Maste #  define _POSIX_VDISABLE       VDISABLE
53*0afa8e06SEd Maste #endif
54*0afa8e06SEd Maste 
55*0afa8e06SEd Maste static volatile sig_atomic_t signo[NSIG];
56*0afa8e06SEd Maste 
57*0afa8e06SEd Maste static void handler(int);
58*0afa8e06SEd Maste 
59*0afa8e06SEd Maste char *
readpassphrase(const char * prompt,char * buf,size_t bufsiz,int flags)60*0afa8e06SEd Maste readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
61*0afa8e06SEd Maste {
62*0afa8e06SEd Maste 	ssize_t nr;
63*0afa8e06SEd Maste 	int input, output, save_errno, i, need_restart;
64*0afa8e06SEd Maste 	char ch, *p, *end;
65*0afa8e06SEd Maste 	struct termios term, oterm;
66*0afa8e06SEd Maste 	struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
67*0afa8e06SEd Maste 	struct sigaction savetstp, savettin, savettou, savepipe;
68*0afa8e06SEd Maste 
69*0afa8e06SEd Maste 	/* I suppose we could alloc on demand in this case (XXX). */
70*0afa8e06SEd Maste 	if (bufsiz == 0) {
71*0afa8e06SEd Maste 		errno = EINVAL;
72*0afa8e06SEd Maste 		return(NULL);
73*0afa8e06SEd Maste 	}
74*0afa8e06SEd Maste 
75*0afa8e06SEd Maste restart:
76*0afa8e06SEd Maste 	for (i = 0; i < NSIG; i++)
77*0afa8e06SEd Maste 		signo[i] = 0;
78*0afa8e06SEd Maste 	need_restart = 0;
79*0afa8e06SEd Maste 	/*
80*0afa8e06SEd Maste 	 * Read and write to /dev/tty if available.  If not, read from
81*0afa8e06SEd Maste 	 * stdin and write to stderr unless a tty is required.
82*0afa8e06SEd Maste 	 */
83*0afa8e06SEd Maste 	if ((flags & RPP_STDIN) ||
84*0afa8e06SEd Maste 	    (input = output = open(_PATH_TTY, O_RDWR)) == -1) {
85*0afa8e06SEd Maste 		if (flags & RPP_REQUIRE_TTY) {
86*0afa8e06SEd Maste 			errno = ENOTTY;
87*0afa8e06SEd Maste 			return(NULL);
88*0afa8e06SEd Maste 		}
89*0afa8e06SEd Maste 		input = STDIN_FILENO;
90*0afa8e06SEd Maste 		output = STDERR_FILENO;
91*0afa8e06SEd Maste 	}
92*0afa8e06SEd Maste 
93*0afa8e06SEd Maste 	/*
94*0afa8e06SEd Maste 	 * Turn off echo if possible.
95*0afa8e06SEd Maste 	 * If we are using a tty but are not the foreground pgrp this will
96*0afa8e06SEd Maste 	 * generate SIGTTOU, so do it *before* installing the signal handlers.
97*0afa8e06SEd Maste 	 */
98*0afa8e06SEd Maste 	if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
99*0afa8e06SEd Maste 		memcpy(&term, &oterm, sizeof(term));
100*0afa8e06SEd Maste 		if (!(flags & RPP_ECHO_ON))
101*0afa8e06SEd Maste 			term.c_lflag &= ~(ECHO | ECHONL);
102*0afa8e06SEd Maste #ifdef VSTATUS
103*0afa8e06SEd Maste 		if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
104*0afa8e06SEd Maste 			term.c_cc[VSTATUS] = _POSIX_VDISABLE;
105*0afa8e06SEd Maste #endif
106*0afa8e06SEd Maste 		(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
107*0afa8e06SEd Maste 	} else {
108*0afa8e06SEd Maste 		memset(&term, 0, sizeof(term));
109*0afa8e06SEd Maste 		term.c_lflag |= ECHO;
110*0afa8e06SEd Maste 		memset(&oterm, 0, sizeof(oterm));
111*0afa8e06SEd Maste 		oterm.c_lflag |= ECHO;
112*0afa8e06SEd Maste 	}
113*0afa8e06SEd Maste 
114*0afa8e06SEd Maste 	/*
115*0afa8e06SEd Maste 	 * Catch signals that would otherwise cause the user to end
116*0afa8e06SEd Maste 	 * up with echo turned off in the shell.  Don't worry about
117*0afa8e06SEd Maste 	 * things like SIGXCPU and SIGVTALRM for now.
118*0afa8e06SEd Maste 	 */
119*0afa8e06SEd Maste 	sigemptyset(&sa.sa_mask);
120*0afa8e06SEd Maste 	sa.sa_flags = 0;		/* don't restart system calls */
121*0afa8e06SEd Maste 	sa.sa_handler = handler;
122*0afa8e06SEd Maste 	(void)sigaction(SIGALRM, &sa, &savealrm);
123*0afa8e06SEd Maste 	(void)sigaction(SIGHUP, &sa, &savehup);
124*0afa8e06SEd Maste 	(void)sigaction(SIGINT, &sa, &saveint);
125*0afa8e06SEd Maste 	(void)sigaction(SIGPIPE, &sa, &savepipe);
126*0afa8e06SEd Maste 	(void)sigaction(SIGQUIT, &sa, &savequit);
127*0afa8e06SEd Maste 	(void)sigaction(SIGTERM, &sa, &saveterm);
128*0afa8e06SEd Maste 	(void)sigaction(SIGTSTP, &sa, &savetstp);
129*0afa8e06SEd Maste 	(void)sigaction(SIGTTIN, &sa, &savettin);
130*0afa8e06SEd Maste 	(void)sigaction(SIGTTOU, &sa, &savettou);
131*0afa8e06SEd Maste 
132*0afa8e06SEd Maste 	if (!(flags & RPP_STDIN))
133*0afa8e06SEd Maste 		(void)write(output, prompt, strlen(prompt));
134*0afa8e06SEd Maste 	end = buf + bufsiz - 1;
135*0afa8e06SEd Maste 	p = buf;
136*0afa8e06SEd Maste 	while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
137*0afa8e06SEd Maste 		if (p < end) {
138*0afa8e06SEd Maste 			if ((flags & RPP_SEVENBIT))
139*0afa8e06SEd Maste 				ch &= 0x7f;
140*0afa8e06SEd Maste 			if (isalpha((unsigned char)ch)) {
141*0afa8e06SEd Maste 				if ((flags & RPP_FORCELOWER))
142*0afa8e06SEd Maste 					ch = (char)tolower((unsigned char)ch);
143*0afa8e06SEd Maste 				if ((flags & RPP_FORCEUPPER))
144*0afa8e06SEd Maste 					ch = (char)toupper((unsigned char)ch);
145*0afa8e06SEd Maste 			}
146*0afa8e06SEd Maste 			*p++ = ch;
147*0afa8e06SEd Maste 		}
148*0afa8e06SEd Maste 	}
149*0afa8e06SEd Maste 	*p = '\0';
150*0afa8e06SEd Maste 	save_errno = errno;
151*0afa8e06SEd Maste 	if (!(term.c_lflag & ECHO))
152*0afa8e06SEd Maste 		(void)write(output, "\n", 1);
153*0afa8e06SEd Maste 
154*0afa8e06SEd Maste 	/* Restore old terminal settings and signals. */
155*0afa8e06SEd Maste 	if (memcmp(&term, &oterm, sizeof(term)) != 0) {
156*0afa8e06SEd Maste 		const int sigttou = signo[SIGTTOU];
157*0afa8e06SEd Maste 
158*0afa8e06SEd Maste 		/* Ignore SIGTTOU generated when we are not the fg pgrp. */
159*0afa8e06SEd Maste 		while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 &&
160*0afa8e06SEd Maste 		    errno == EINTR && !signo[SIGTTOU])
161*0afa8e06SEd Maste 			continue;
162*0afa8e06SEd Maste 		signo[SIGTTOU] = sigttou;
163*0afa8e06SEd Maste 	}
164*0afa8e06SEd Maste 	(void)sigaction(SIGALRM, &savealrm, NULL);
165*0afa8e06SEd Maste 	(void)sigaction(SIGHUP, &savehup, NULL);
166*0afa8e06SEd Maste 	(void)sigaction(SIGINT, &saveint, NULL);
167*0afa8e06SEd Maste 	(void)sigaction(SIGQUIT, &savequit, NULL);
168*0afa8e06SEd Maste 	(void)sigaction(SIGPIPE, &savepipe, NULL);
169*0afa8e06SEd Maste 	(void)sigaction(SIGTERM, &saveterm, NULL);
170*0afa8e06SEd Maste 	(void)sigaction(SIGTSTP, &savetstp, NULL);
171*0afa8e06SEd Maste 	(void)sigaction(SIGTTIN, &savettin, NULL);
172*0afa8e06SEd Maste 	(void)sigaction(SIGTTOU, &savettou, NULL);
173*0afa8e06SEd Maste 	if (input != STDIN_FILENO)
174*0afa8e06SEd Maste 		(void)close(input);
175*0afa8e06SEd Maste 
176*0afa8e06SEd Maste 	/*
177*0afa8e06SEd Maste 	 * If we were interrupted by a signal, resend it to ourselves
178*0afa8e06SEd Maste 	 * now that we have restored the signal handlers.
179*0afa8e06SEd Maste 	 */
180*0afa8e06SEd Maste 	for (i = 0; i < NSIG; i++) {
181*0afa8e06SEd Maste 		if (signo[i]) {
182*0afa8e06SEd Maste 			kill(getpid(), i);
183*0afa8e06SEd Maste 			switch (i) {
184*0afa8e06SEd Maste 			case SIGTSTP:
185*0afa8e06SEd Maste 			case SIGTTIN:
186*0afa8e06SEd Maste 			case SIGTTOU:
187*0afa8e06SEd Maste 				need_restart = 1;
188*0afa8e06SEd Maste 			}
189*0afa8e06SEd Maste 		}
190*0afa8e06SEd Maste 	}
191*0afa8e06SEd Maste 	if (need_restart)
192*0afa8e06SEd Maste 		goto restart;
193*0afa8e06SEd Maste 
194*0afa8e06SEd Maste 	if (save_errno)
195*0afa8e06SEd Maste 		errno = save_errno;
196*0afa8e06SEd Maste 	return(nr == -1 ? NULL : buf);
197*0afa8e06SEd Maste }
198*0afa8e06SEd Maste 
199*0afa8e06SEd Maste #if 0
200*0afa8e06SEd Maste char *
201*0afa8e06SEd Maste getpass(const char *prompt)
202*0afa8e06SEd Maste {
203*0afa8e06SEd Maste 	static char buf[_PASSWORD_LEN + 1];
204*0afa8e06SEd Maste 
205*0afa8e06SEd Maste 	return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
206*0afa8e06SEd Maste }
207*0afa8e06SEd Maste #endif
208*0afa8e06SEd Maste 
handler(int s)209*0afa8e06SEd Maste static void handler(int s)
210*0afa8e06SEd Maste {
211*0afa8e06SEd Maste 
212*0afa8e06SEd Maste 	signo[s] = 1;
213*0afa8e06SEd Maste }
214*0afa8e06SEd Maste #endif /* HAVE_READPASSPHRASE */
215