xref: /netbsd-src/crypto/external/bsd/openssh/dist/readpass.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: readpass.c,v 1.9 2017/04/18 18:41:46 christos Exp $	*/
2 /* $OpenBSD: readpass.c,v 1.51 2015/12/11 00:20:04 mmcc Exp $ */
3 
4 /*
5  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "includes.h"
29 __RCSID("$NetBSD: readpass.c,v 1.9 2017/04/18 18:41:46 christos Exp $");
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <paths.h>
36 #include <readpassphrase.h>
37 #include <signal.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #include "xmalloc.h"
45 #include "misc.h"
46 #include "pathnames.h"
47 #include "log.h"
48 #include "ssh.h"
49 #include "uidswap.h"
50 
51 static char *
52 ssh_askpass(const char *askpass, const char *msg)
53 {
54 	pid_t pid, ret;
55 	size_t len;
56 	char *pass;
57 	int p[2], status;
58 	char buf[1024];
59 	void (*osigchld)(int);
60 
61 	if (fflush(stdout) != 0)
62 		error("ssh_askpass: fflush: %s", strerror(errno));
63 	if (askpass == NULL)
64 		fatal("internal error: askpass undefined");
65 	if (pipe(p) < 0) {
66 		error("ssh_askpass: pipe: %s", strerror(errno));
67 		return NULL;
68 	}
69 	osigchld = signal(SIGCHLD, SIG_DFL);
70 	if ((pid = fork()) < 0) {
71 		error("ssh_askpass: fork: %s", strerror(errno));
72 		signal(SIGCHLD, osigchld);
73 		return NULL;
74 	}
75 	if (pid == 0) {
76 		permanently_drop_suid(getuid());
77 		close(p[0]);
78 		if (dup2(p[1], STDOUT_FILENO) < 0)
79 			fatal("ssh_askpass: dup2: %s", strerror(errno));
80 		execlp(askpass, askpass, msg, (char *)NULL);
81 		fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno));
82 	}
83 	close(p[1]);
84 
85 	len = 0;
86 	do {
87 		ssize_t r = read(p[0], buf + len, sizeof(buf) - 1 - len);
88 
89 		if (r == -1 && errno == EINTR)
90 			continue;
91 		if (r <= 0)
92 			break;
93 		len += r;
94 	} while (sizeof(buf) - 1 - len > 0);
95 	buf[len] = '\0';
96 
97 	close(p[0]);
98 	while ((ret = waitpid(pid, &status, 0)) < 0)
99 		if (errno != EINTR)
100 			break;
101 	signal(SIGCHLD, osigchld);
102 	if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
103 		explicit_bzero(buf, sizeof(buf));
104 		return NULL;
105 	}
106 
107 	buf[strcspn(buf, "\r\n")] = '\0';
108 	pass = xstrdup(buf);
109 	explicit_bzero(buf, sizeof(buf));
110 	return pass;
111 }
112 
113 /*
114  * Reads a passphrase from /dev/tty with echo turned off/on.  Returns the
115  * passphrase (allocated with xmalloc).  Exits if EOF is encountered. If
116  * RP_ALLOW_STDIN is set, the passphrase will be read from stdin if no
117  * tty is available
118  */
119 char *
120 read_passphrase(const char *prompt, int flags)
121 {
122 	const char *askpass = NULL;
123 	char *ret, buf[1024];
124 	int rppflags, use_askpass = 0, ttyfd;
125 
126 	rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF;
127 	if (flags & RP_USE_ASKPASS)
128 		use_askpass = 1;
129 	else if (flags & RP_ALLOW_STDIN) {
130 		if (!isatty(STDIN_FILENO)) {
131 			debug("read_passphrase: stdin is not a tty");
132 			use_askpass = 1;
133 		}
134 	} else {
135 		rppflags |= RPP_REQUIRE_TTY;
136 		ttyfd = open(_PATH_TTY, O_RDWR);
137 		if (ttyfd >= 0)
138 			close(ttyfd);
139 		else {
140 			debug("read_passphrase: can't open %s: %s", _PATH_TTY,
141 			    strerror(errno));
142 			use_askpass = 1;
143 		}
144 	}
145 
146 	if ((flags & RP_USE_ASKPASS) && getenv("DISPLAY") == NULL)
147 		return (flags & RP_ALLOW_EOF) ? NULL : xstrdup("");
148 
149 	if (use_askpass && getenv("DISPLAY")) {
150 		if (getenv(SSH_ASKPASS_ENV))
151 			askpass = getenv(SSH_ASKPASS_ENV);
152 		else
153 			askpass = _PATH_SSH_ASKPASS_DEFAULT;
154 		if ((ret = ssh_askpass(askpass, prompt)) == NULL)
155 			if (!(flags & RP_ALLOW_EOF))
156 				return xstrdup("");
157 		return ret;
158 	}
159 
160 	if (readpassphrase(prompt, buf, sizeof buf, rppflags) == NULL) {
161 		if (flags & RP_ALLOW_EOF)
162 			return NULL;
163 		return xstrdup("");
164 	}
165 
166 	ret = xstrdup(buf);
167 	explicit_bzero(buf, sizeof(buf));
168 	return ret;
169 }
170 
171 int
172 ask_permission(const char *fmt, ...)
173 {
174 	va_list args;
175 	char *p, prompt[1024];
176 	int allowed = 0;
177 
178 	va_start(args, fmt);
179 	vsnprintf(prompt, sizeof(prompt), fmt, args);
180 	va_end(args);
181 
182 	p = read_passphrase(prompt, RP_USE_ASKPASS|RP_ALLOW_EOF);
183 	if (p != NULL) {
184 		/*
185 		 * Accept empty responses and responses consisting
186 		 * of the word "yes" as affirmative.
187 		 */
188 		if (*p == '\0' || *p == '\n' ||
189 		    strcasecmp(p, "yes") == 0)
190 			allowed = 1;
191 		free(p);
192 	}
193 
194 	return (allowed);
195 }
196