1299425Smm/*-
2299425Smm * Copyright (c) 2014 Michihiro NAKAJIMA
3299425Smm * All rights reserved.
4299425Smm *
5299425Smm * Redistribution and use in source and binary forms, with or without
6299425Smm * modification, are permitted provided that the following conditions
7299425Smm * are met:
8299425Smm * 1. Redistributions of source code must retain the above copyright
9299425Smm *    notice, this list of conditions and the following disclaimer
10299425Smm *    in this position and unchanged.
11299425Smm * 2. Redistributions in binary form must reproduce the above copyright
12299425Smm *    notice, this list of conditions and the following disclaimer in the
13299425Smm *    documentation and/or other materials provided with the distribution.
14299425Smm *
15299425Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16299425Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17299425Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18299425Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19299425Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20299425Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21299425Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22299425Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23299425Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24299425Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25299425Smm */
26349524Smm/*	$OpenBSD: readpassphrase.c,v 1.27 2019/01/25 00:19:25 millert Exp $	*/
27349524Smm
28299425Smm/*
29349524Smm * Copyright (c) 2000-2002, 2007, 2010
30349524Smm * 	Todd C. Miller <millert@openbsd.org>
31299425Smm *
32299425Smm * Permission to use, copy, modify, and distribute this software for any
33299425Smm * purpose with or without fee is hereby granted, provided that the above
34299425Smm * copyright notice and this permission notice appear in all copies.
35299425Smm *
36299425Smm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
37299425Smm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
38299425Smm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
39299425Smm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40299425Smm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
41299425Smm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
42299425Smm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43299425Smm *
44299425Smm * Sponsored in part by the Defense Advanced Research Projects
45299425Smm * Agency (DARPA) and Air Force Research Laboratory, Air Force
46299425Smm * Materiel Command, USAF, under agreement number F39502-99-1-0512.
47299425Smm */
48299425Smm
49299425Smm/* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */
50299425Smm
51299425Smm
52299425Smm#include "lafe_platform.h"
53299425Smm__FBSDID("$FreeBSD: stable/11/contrib/libarchive/libarchive_fe/passphrase.c 349524 2019-06-28 22:33:44Z mm $");
54299425Smm
55299425Smm#include <errno.h>
56299425Smm#ifdef HAVE_STDLIB_H
57299425Smm#include <stdlib.h>
58299425Smm#endif
59299425Smm#ifdef HAVE_UNISTD_H
60299425Smm#include <unistd.h>
61299425Smm#endif
62299425Smm#ifdef HAVE_READPASSPHRASE_H
63299425Smm#include <readpassphrase.h>
64299425Smm#endif
65299425Smm
66299425Smm#include "err.h"
67299425Smm#include "passphrase.h"
68299425Smm
69299425Smm#ifndef HAVE_READPASSPHRASE
70299425Smm
71299425Smm#define RPP_ECHO_OFF    0x00		/* Turn off echo (default). */
72299425Smm#define RPP_ECHO_ON     0x01		/* Leave echo on. */
73299425Smm#define RPP_REQUIRE_TTY 0x02		/* Fail if there is no tty. */
74299425Smm#define RPP_FORCELOWER  0x04		/* Force input to lower case. */
75299425Smm#define RPP_FORCEUPPER  0x08		/* Force input to upper case. */
76299425Smm#define RPP_SEVENBIT    0x10		/* Strip the high bit from input. */
77299425Smm#define RPP_STDIN       0x20		/* Read from stdin, not /dev/tty */
78299425Smm
79299425Smm
80299425Smm#if defined(_WIN32) && !defined(__CYGWIN__)
81299425Smm#include <windows.h>
82299425Smm
83299425Smmstatic char *
84299425Smmreadpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
85299425Smm{
86299425Smm	HANDLE hStdin, hStdout;
87299425Smm	DWORD mode, rbytes;
88299425Smm	BOOL success;
89299425Smm
90299425Smm	(void)flags;
91299425Smm
92299425Smm	hStdin = GetStdHandle(STD_INPUT_HANDLE);
93299425Smm	if (hStdin == INVALID_HANDLE_VALUE)
94299425Smm		return (NULL);
95299425Smm	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
96299425Smm	if (hStdout == INVALID_HANDLE_VALUE)
97299425Smm		return (NULL);
98299425Smm
99299425Smm	success = GetConsoleMode(hStdin, &mode);
100299425Smm	if (!success)
101299425Smm		return (NULL);
102299425Smm	mode &= ~ENABLE_ECHO_INPUT;
103299425Smm	mode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
104299425Smm	success = SetConsoleMode(hStdin, mode);
105299425Smm	if (!success)
106299425Smm		return (NULL);
107299425Smm
108299425Smm	success = WriteFile(hStdout, prompt, (DWORD)strlen(prompt),
109299425Smm		NULL, NULL);
110299425Smm	if (!success)
111299425Smm		return (NULL);
112299425Smm	success = ReadFile(hStdin, buf, (DWORD)bufsiz - 1, &rbytes, NULL);
113299425Smm	if (!success)
114299425Smm		return (NULL);
115299425Smm	WriteFile(hStdout, "\r\n", 2, NULL, NULL);
116299425Smm	buf[rbytes] = '\0';
117299425Smm	/* Remove trailing carriage return(s). */
118299425Smm	if (rbytes > 2 && buf[rbytes - 2] == '\r' && buf[rbytes - 1] == '\n')
119299425Smm		buf[rbytes - 2] = '\0';
120299425Smm
121299425Smm	return (buf);
122299425Smm}
123299425Smm
124299425Smm#else /* _WIN32 && !__CYGWIN__ */
125299425Smm
126302075Smm#include <assert.h>
127299425Smm#include <ctype.h>
128299425Smm#include <fcntl.h>
129299425Smm#ifdef HAVE_PATHS_H
130299425Smm#include <paths.h>
131299425Smm#endif
132302075Smm#include <signal.h>
133299425Smm#include <string.h>
134302075Smm#include <termios.h>
135299425Smm#include <unistd.h>
136299425Smm
137302294Smm#ifndef _PATH_TTY
138302294Smm#define _PATH_TTY "/dev/tty"
139302294Smm#endif
140302294Smm
141299425Smm#ifdef TCSASOFT
142299425Smm# define _T_FLUSH	(TCSAFLUSH|TCSASOFT)
143299425Smm#else
144299425Smm# define _T_FLUSH	(TCSAFLUSH)
145299425Smm#endif
146299425Smm
147299425Smm/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */
148299425Smm#if !defined(_POSIX_VDISABLE) && defined(VDISABLE)
149299425Smm#  define _POSIX_VDISABLE       VDISABLE
150299425Smm#endif
151299425Smm
152302075Smm#define M(a,b) (a > b ? a : b)
153302075Smm#define MAX_SIGNO M(M(M(SIGALRM, SIGHUP), \
154302075Smm                      M(SIGINT, SIGPIPE)), \
155302075Smm                    M(M(SIGQUIT, SIGTERM), \
156302075Smm                      M(M(SIGTSTP, SIGTTIN), SIGTTOU)))
157299425Smm
158302075Smmstatic volatile sig_atomic_t signo[MAX_SIGNO + 1];
159302075Smm
160299425Smmstatic void
161299425Smmhandler(int s)
162299425Smm{
163302075Smm	assert(s <= MAX_SIGNO);
164299425Smm	signo[s] = 1;
165299425Smm}
166299425Smm
167299425Smmstatic char *
168299425Smmreadpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
169299425Smm{
170299425Smm	ssize_t nr;
171299425Smm	int input, output, save_errno, i, need_restart;
172299425Smm	char ch, *p, *end;
173299425Smm	struct termios term, oterm;
174299425Smm	struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
175299425Smm	struct sigaction savetstp, savettin, savettou, savepipe;
176299425Smm
177299425Smm	/* I suppose we could alloc on demand in this case (XXX). */
178299425Smm	if (bufsiz == 0) {
179299425Smm		errno = EINVAL;
180299425Smm		return(NULL);
181299425Smm	}
182299425Smm
183299425Smmrestart:
184302075Smm	for (i = 0; i <= MAX_SIGNO; i++)
185299425Smm		signo[i] = 0;
186299425Smm	nr = -1;
187299425Smm	save_errno = 0;
188299425Smm	need_restart = 0;
189299425Smm	/*
190299425Smm	 * Read and write to /dev/tty if available.  If not, read from
191299425Smm	 * stdin and write to stderr unless a tty is required.
192299425Smm	 */
193299425Smm	if ((flags & RPP_STDIN) ||
194299425Smm	    (input = output = open(_PATH_TTY, O_RDWR)) == -1) {
195299425Smm		if (flags & RPP_REQUIRE_TTY) {
196299425Smm			errno = ENOTTY;
197299425Smm			return(NULL);
198299425Smm		}
199299425Smm		input = STDIN_FILENO;
200299425Smm		output = STDERR_FILENO;
201299425Smm	}
202299425Smm
203299425Smm	/*
204349524Smm	 * Turn off echo if possible.
205349524Smm	 * If we are using a tty but are not the foreground pgrp this will
206349524Smm	 * generate SIGTTOU, so do it *before* installing the signal handlers.
207349524Smm	 */
208349524Smm	if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
209349524Smm		memcpy(&term, &oterm, sizeof(term));
210349524Smm		if (!(flags & RPP_ECHO_ON))
211349524Smm			term.c_lflag &= ~(ECHO | ECHONL);
212349524Smm#ifdef VSTATUS
213349524Smm		if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
214349524Smm			term.c_cc[VSTATUS] = _POSIX_VDISABLE;
215349524Smm#endif
216349524Smm		(void)tcsetattr(input, _T_FLUSH, &term);
217349524Smm	} else {
218349524Smm		memset(&term, 0, sizeof(term));
219349524Smm		term.c_lflag |= ECHO;
220349524Smm		memset(&oterm, 0, sizeof(oterm));
221349524Smm		oterm.c_lflag |= ECHO;
222349524Smm	}
223349524Smm
224349524Smm	/*
225299425Smm	 * Catch signals that would otherwise cause the user to end
226299425Smm	 * up with echo turned off in the shell.  Don't worry about
227299425Smm	 * things like SIGXCPU and SIGVTALRM for now.
228299425Smm	 */
229299425Smm	sigemptyset(&sa.sa_mask);
230299425Smm	sa.sa_flags = 0;		/* don't restart system calls */
231299425Smm	sa.sa_handler = handler;
232302075Smm	/* Keep this list in sync with MAX_SIGNO! */
233299425Smm	(void)sigaction(SIGALRM, &sa, &savealrm);
234299425Smm	(void)sigaction(SIGHUP, &sa, &savehup);
235299425Smm	(void)sigaction(SIGINT, &sa, &saveint);
236299425Smm	(void)sigaction(SIGPIPE, &sa, &savepipe);
237299425Smm	(void)sigaction(SIGQUIT, &sa, &savequit);
238299425Smm	(void)sigaction(SIGTERM, &sa, &saveterm);
239299425Smm	(void)sigaction(SIGTSTP, &sa, &savetstp);
240299425Smm	(void)sigaction(SIGTTIN, &sa, &savettin);
241299425Smm	(void)sigaction(SIGTTOU, &sa, &savettou);
242299425Smm
243349524Smm	if (!(flags & RPP_STDIN)) {
244349524Smm		int r = write(output, prompt, strlen(prompt));
245349524Smm		(void)r;
246299425Smm	}
247349524Smm	end = buf + bufsiz - 1;
248349524Smm	p = buf;
249349524Smm	while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
250349524Smm		if (p < end) {
251349524Smm			if ((flags & RPP_SEVENBIT))
252349524Smm				ch &= 0x7f;
253349524Smm			if (isalpha((unsigned char)ch)) {
254349524Smm				if ((flags & RPP_FORCELOWER))
255349524Smm					ch = (char)tolower((unsigned char)ch);
256349524Smm				if ((flags & RPP_FORCEUPPER))
257349524Smm					ch = (char)toupper((unsigned char)ch);
258299425Smm			}
259349524Smm			*p++ = ch;
260299425Smm		}
261299425Smm	}
262349524Smm	*p = '\0';
263349524Smm	save_errno = errno;
264349524Smm	if (!(term.c_lflag & ECHO)) {
265349524Smm		int r = write(output, "\n", 1);
266349524Smm		(void)r;
267349524Smm	}
268299425Smm
269299425Smm	/* Restore old terminal settings and signals. */
270299425Smm	if (memcmp(&term, &oterm, sizeof(term)) != 0) {
271349524Smm		const int sigttou = signo[SIGTTOU];
272349524Smm
273349524Smm		/* Ignore SIGTTOU generated when we are not the fg pgrp. */
274299425Smm		while (tcsetattr(input, _T_FLUSH, &oterm) == -1 &&
275349524Smm		    errno == EINTR && !signo[SIGTTOU])
276299425Smm			continue;
277349524Smm		signo[SIGTTOU] = sigttou;
278299425Smm	}
279299425Smm	(void)sigaction(SIGALRM, &savealrm, NULL);
280299425Smm	(void)sigaction(SIGHUP, &savehup, NULL);
281299425Smm	(void)sigaction(SIGINT, &saveint, NULL);
282299425Smm	(void)sigaction(SIGQUIT, &savequit, NULL);
283299425Smm	(void)sigaction(SIGPIPE, &savepipe, NULL);
284299425Smm	(void)sigaction(SIGTERM, &saveterm, NULL);
285299425Smm	(void)sigaction(SIGTSTP, &savetstp, NULL);
286299425Smm	(void)sigaction(SIGTTIN, &savettin, NULL);
287299425Smm	(void)sigaction(SIGTTOU, &savettou, NULL);
288299425Smm	if (input != STDIN_FILENO)
289299425Smm		(void)close(input);
290299425Smm
291299425Smm	/*
292299425Smm	 * If we were interrupted by a signal, resend it to ourselves
293299425Smm	 * now that we have restored the signal handlers.
294299425Smm	 */
295302075Smm	for (i = 0; i <= MAX_SIGNO; i++) {
296299425Smm		if (signo[i]) {
297299425Smm			kill(getpid(), i);
298299425Smm			switch (i) {
299299425Smm			case SIGTSTP:
300299425Smm			case SIGTTIN:
301299425Smm			case SIGTTOU:
302299425Smm				need_restart = 1;
303299425Smm			}
304299425Smm		}
305299425Smm	}
306299425Smm	if (need_restart)
307299425Smm		goto restart;
308299425Smm
309299425Smm	if (save_errno)
310299425Smm		errno = save_errno;
311299425Smm	return(nr == -1 ? NULL : buf);
312299425Smm}
313299425Smm#endif /* _WIN32 && !__CYGWIN__ */
314299425Smm#endif /* HAVE_READPASSPHRASE */
315299425Smm
316299425Smmchar *
317299425Smmlafe_readpassphrase(const char *prompt, char *buf, size_t bufsiz)
318299425Smm{
319299425Smm	char *p;
320299425Smm
321299425Smm	p = readpassphrase(prompt, buf, bufsiz, RPP_ECHO_OFF);
322299425Smm	if (p == NULL) {
323299425Smm		switch (errno) {
324299425Smm		case EINTR:
325299425Smm			break;
326299425Smm		default:
327299425Smm			lafe_errc(1, errno, "Couldn't read passphrase");
328299425Smm			break;
329299425Smm		}
330299425Smm	}
331299425Smm	return (p);
332299425Smm}
333299425Smm
334