xref: /openssh-portable/readpass.c (revision 4340dd43)
1*4340dd43Sclaudio@openbsd.org /* $OpenBSD: readpass.c,v 1.68 2020/11/10 07:46:20 claudio Exp $ */
2d4a8b7e3SDamien Miller /*
33606ee29SDamien Miller  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
495def098SDamien Miller  *
550945fa8SDamien Miller  * Redistribution and use in source and binary forms, with or without
650945fa8SDamien Miller  * modification, are permitted provided that the following conditions
750945fa8SDamien Miller  * are met:
850945fa8SDamien Miller  * 1. Redistributions of source code must retain the above copyright
950945fa8SDamien Miller  *    notice, this list of conditions and the following disclaimer.
1050945fa8SDamien Miller  * 2. Redistributions in binary form must reproduce the above copyright
1150945fa8SDamien Miller  *    notice, this list of conditions and the following disclaimer in the
1250945fa8SDamien Miller  *    documentation and/or other materials provided with the distribution.
1395def098SDamien Miller  *
143606ee29SDamien Miller  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
153606ee29SDamien Miller  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
163606ee29SDamien Miller  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
173606ee29SDamien Miller  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
183606ee29SDamien Miller  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
193606ee29SDamien Miller  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
203606ee29SDamien Miller  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
213606ee29SDamien Miller  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
223606ee29SDamien Miller  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
233606ee29SDamien Miller  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24d4a8b7e3SDamien Miller  */
25d4a8b7e3SDamien Miller 
26d4a8b7e3SDamien Miller #include "includes.h"
279cf6d077SDamien Miller 
289cf6d077SDamien Miller #include <sys/types.h>
299cf6d077SDamien Miller #include <sys/wait.h>
3003e2003aSDamien Miller 
3139972493SDarren Tucker #include <errno.h>
3257cf6385SDamien Miller #include <fcntl.h>
3303e2003aSDamien Miller #ifdef HAVE_PATHS_H
3403e2003aSDamien Miller # include <paths.h>
3503e2003aSDamien Miller #endif
36106079c0SDamien Miller #include <signal.h>
375d19626aSDarren Tucker #include <stdarg.h>
38a7a73ee3SDamien Miller #include <stdio.h>
39e7a1e5cfSDamien Miller #include <stdlib.h>
40e3476ed0SDamien Miller #include <string.h>
41e6b3b610SDamien Miller #include <unistd.h>
42949974bbSBen Lindstrom 
43d4a8b7e3SDamien Miller #include "xmalloc.h"
44e608ca29SDarren Tucker #include "misc.h"
455eb97b6fSBen Lindstrom #include "pathnames.h"
465eb97b6fSBen Lindstrom #include "log.h"
475eb97b6fSBen Lindstrom #include "ssh.h"
486b4069adSDamien Miller #include "uidswap.h"
495eb97b6fSBen Lindstrom 
50bba81213SBen Lindstrom static char *
ssh_askpass(char * askpass,const char * msg,const char * env_hint)5159175a35Sdjm@openbsd.org ssh_askpass(char *askpass, const char *msg, const char *env_hint)
525eb97b6fSBen Lindstrom {
53106079c0SDamien Miller 	pid_t pid, ret;
545eb97b6fSBen Lindstrom 	size_t len;
55637b8ae2SDamien Miller 	char *pass;
56106079c0SDamien Miller 	int p[2], status;
575eb97b6fSBen Lindstrom 	char buf[1024];
58106079c0SDamien Miller 	void (*osigchld)(int);
595eb97b6fSBen Lindstrom 
605eb97b6fSBen Lindstrom 	if (fflush(stdout) != 0)
61816036f1Sdjm@openbsd.org 		error_f("fflush: %s", strerror(errno));
625eb97b6fSBen Lindstrom 	if (askpass == NULL)
635eb97b6fSBen Lindstrom 		fatal("internal error: askpass undefined");
644d28fa78Sderaadt@openbsd.org 	if (pipe(p) == -1) {
65816036f1Sdjm@openbsd.org 		error_f("pipe: %s", strerror(errno));
666c71179fSDamien Miller 		return NULL;
6707ab49efSDamien Miller 	}
683bf2a6acSdtucker@openbsd.org 	osigchld = ssh_signal(SIGCHLD, SIG_DFL);
694d28fa78Sderaadt@openbsd.org 	if ((pid = fork()) == -1) {
70816036f1Sdjm@openbsd.org 		error_f("fork: %s", strerror(errno));
713bf2a6acSdtucker@openbsd.org 		ssh_signal(SIGCHLD, osigchld);
726c71179fSDamien Miller 		return NULL;
7307ab49efSDamien Miller 	}
745eb97b6fSBen Lindstrom 	if (pid == 0) {
755eb97b6fSBen Lindstrom 		close(p[0]);
764d28fa78Sderaadt@openbsd.org 		if (dup2(p[1], STDOUT_FILENO) == -1)
77816036f1Sdjm@openbsd.org 			fatal_f("dup2: %s", strerror(errno));
7859175a35Sdjm@openbsd.org 		if (env_hint != NULL)
7959175a35Sdjm@openbsd.org 			setenv("SSH_ASKPASS_PROMPT", env_hint, 1);
8094141b7aSmmcc@openbsd.org 		execlp(askpass, askpass, msg, (char *)NULL);
81816036f1Sdjm@openbsd.org 		fatal_f("exec(%s): %s", askpass, strerror(errno));
825eb97b6fSBen Lindstrom 	}
835eb97b6fSBen Lindstrom 	close(p[1]);
84f451e22eSDamien Miller 
85106079c0SDamien Miller 	len = 0;
86f451e22eSDamien Miller 	do {
87106079c0SDamien Miller 		ssize_t r = read(p[0], buf + len, sizeof(buf) - 1 - len);
88106079c0SDamien Miller 
89106079c0SDamien Miller 		if (r == -1 && errno == EINTR)
90f451e22eSDamien Miller 			continue;
91106079c0SDamien Miller 		if (r <= 0)
92f451e22eSDamien Miller 			break;
93106079c0SDamien Miller 		len += r;
94f451e22eSDamien Miller 	} while (sizeof(buf) - 1 - len > 0);
95f451e22eSDamien Miller 	buf[len] = '\0';
96f451e22eSDamien Miller 
975eb97b6fSBen Lindstrom 	close(p[0]);
984d28fa78Sderaadt@openbsd.org 	while ((ret = waitpid(pid, &status, 0)) == -1)
995eb97b6fSBen Lindstrom 		if (errno != EINTR)
1005eb97b6fSBen Lindstrom 			break;
1013bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGCHLD, osigchld);
102106079c0SDamien Miller 	if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
103a5103f41SDamien Miller 		explicit_bzero(buf, sizeof(buf));
1046c71179fSDamien Miller 		return NULL;
1056c71179fSDamien Miller 	}
1066c71179fSDamien Miller 
107637b8ae2SDamien Miller 	buf[strcspn(buf, "\r\n")] = '\0';
1085eb97b6fSBen Lindstrom 	pass = xstrdup(buf);
109a5103f41SDamien Miller 	explicit_bzero(buf, sizeof(buf));
1105eb97b6fSBen Lindstrom 	return pass;
1115eb97b6fSBen Lindstrom }
1125eb97b6fSBen Lindstrom 
11359175a35Sdjm@openbsd.org /* private/internal read_passphrase flags */
11459175a35Sdjm@openbsd.org #define RP_ASK_PERMISSION	0x8000 /* pass hint to askpass for confirm UI */
11559175a35Sdjm@openbsd.org 
1165428f646SDamien Miller /*
117949974bbSBen Lindstrom  * Reads a passphrase from /dev/tty with echo turned off/on.  Returns the
118949974bbSBen Lindstrom  * passphrase (allocated with xmalloc).  Exits if EOF is encountered. If
119949974bbSBen Lindstrom  * RP_ALLOW_STDIN is set, the passphrase will be read from stdin if no
120949974bbSBen Lindstrom  * tty is available
121874d77bbSDamien Miller  */
12295def098SDamien Miller char *
read_passphrase(const char * prompt,int flags)123949974bbSBen Lindstrom read_passphrase(const char *prompt, int flags)
124d4a8b7e3SDamien Miller {
1254ae7f80dStb@openbsd.org 	char cr = '\r', *askpass = NULL, *ret, buf[1024];
126aaa8b609Sdjm@openbsd.org 	int rppflags, ttyfd, use_askpass = 0, allow_askpass = 0;
12759175a35Sdjm@openbsd.org 	const char *askpass_hint = NULL;
128aaa8b609Sdjm@openbsd.org 	const char *s;
129aaa8b609Sdjm@openbsd.org 
130aaa8b609Sdjm@openbsd.org 	if ((s = getenv("DISPLAY")) != NULL)
131aaa8b609Sdjm@openbsd.org 		allow_askpass = *s != '\0';
132aaa8b609Sdjm@openbsd.org 	if ((s = getenv(SSH_ASKPASS_REQUIRE_ENV)) != NULL) {
133aaa8b609Sdjm@openbsd.org 		if (strcasecmp(s, "force") == 0) {
134aaa8b609Sdjm@openbsd.org 			use_askpass = 1;
135aaa8b609Sdjm@openbsd.org 			allow_askpass = 1;
136aaa8b609Sdjm@openbsd.org 		} else if (strcasecmp(s, "prefer") == 0)
137aaa8b609Sdjm@openbsd.org 			use_askpass = allow_askpass;
138aaa8b609Sdjm@openbsd.org 		else if (strcasecmp(s, "never") == 0)
139aaa8b609Sdjm@openbsd.org 			allow_askpass = 0;
140aaa8b609Sdjm@openbsd.org 	}
1415eb97b6fSBen Lindstrom 
142949974bbSBen Lindstrom 	rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF;
143aaa8b609Sdjm@openbsd.org 	if (use_askpass)
144816036f1Sdjm@openbsd.org 		debug_f("requested to askpass");
145aaa8b609Sdjm@openbsd.org 	else if (flags & RP_USE_ASKPASS)
14623f0770aSDamien Miller 		use_askpass = 1;
14723f0770aSDamien Miller 	else if (flags & RP_ALLOW_STDIN) {
148d2ebd450SDamien Miller 		if (!isatty(STDIN_FILENO)) {
149ddeb7529SDamien Miller 			debug("read_passphrase: stdin is not a tty");
1505eb97b6fSBen Lindstrom 			use_askpass = 1;
151d2ebd450SDamien Miller 		}
1525eb97b6fSBen Lindstrom 	} else {
153949974bbSBen Lindstrom 		rppflags |= RPP_REQUIRE_TTY;
15485830d1dSDamien Miller 		ttyfd = open(_PATH_TTY, O_RDWR);
1554ae7f80dStb@openbsd.org 		if (ttyfd >= 0) {
1564ae7f80dStb@openbsd.org 			/*
1574ae7f80dStb@openbsd.org 			 * If we're on a tty, ensure that show the prompt at
1584ae7f80dStb@openbsd.org 			 * the beginning of the line. This will hopefully
1594ae7f80dStb@openbsd.org 			 * clobber any password characters the user has
1604ae7f80dStb@openbsd.org 			 * optimistically typed before echo is disabled.
1614ae7f80dStb@openbsd.org 			 */
1624ae7f80dStb@openbsd.org 			(void)write(ttyfd, &cr, 1);
1635eb97b6fSBen Lindstrom 			close(ttyfd);
1644ae7f80dStb@openbsd.org 		} else {
165ddeb7529SDamien Miller 			debug("read_passphrase: can't open %s: %s", _PATH_TTY,
166ddeb7529SDamien Miller 			    strerror(errno));
1675eb97b6fSBen Lindstrom 			use_askpass = 1;
1685eb97b6fSBen Lindstrom 		}
169ddeb7529SDamien Miller 	}
1705eb97b6fSBen Lindstrom 
171aaa8b609Sdjm@openbsd.org 	if ((flags & RP_USE_ASKPASS) && !allow_askpass)
17223f0770aSDamien Miller 		return (flags & RP_ALLOW_EOF) ? NULL : xstrdup("");
17323f0770aSDamien Miller 
174aaa8b609Sdjm@openbsd.org 	if (use_askpass && allow_askpass) {
1755eb97b6fSBen Lindstrom 		if (getenv(SSH_ASKPASS_ENV))
1765eb97b6fSBen Lindstrom 			askpass = getenv(SSH_ASKPASS_ENV);
1775eb97b6fSBen Lindstrom 		else
1785eb97b6fSBen Lindstrom 			askpass = _PATH_SSH_ASKPASS_DEFAULT;
17959175a35Sdjm@openbsd.org 		if ((flags & RP_ASK_PERMISSION) != 0)
18059175a35Sdjm@openbsd.org 			askpass_hint = "confirm";
18159175a35Sdjm@openbsd.org 		if ((ret = ssh_askpass(askpass, prompt, askpass_hint)) == NULL)
1826c71179fSDamien Miller 			if (!(flags & RP_ALLOW_EOF))
1836c71179fSDamien Miller 				return xstrdup("");
1846c71179fSDamien Miller 		return ret;
1855eb97b6fSBen Lindstrom 	}
1865eb97b6fSBen Lindstrom 
18738a69e6bSBen Lindstrom 	if (readpassphrase(prompt, buf, sizeof buf, rppflags) == NULL) {
18838a69e6bSBen Lindstrom 		if (flags & RP_ALLOW_EOF)
18938a69e6bSBen Lindstrom 			return NULL;
1904f42d8cdSBen Lindstrom 		return xstrdup("");
19138a69e6bSBen Lindstrom 	}
192949974bbSBen Lindstrom 
193949974bbSBen Lindstrom 	ret = xstrdup(buf);
194a5103f41SDamien Miller 	explicit_bzero(buf, sizeof(buf));
195949974bbSBen Lindstrom 	return ret;
196d4a8b7e3SDamien Miller }
197ce327b62SDarren Tucker 
198ce327b62SDarren Tucker int
ask_permission(const char * fmt,...)199ce327b62SDarren Tucker ask_permission(const char *fmt, ...)
200ce327b62SDarren Tucker {
201ce327b62SDarren Tucker 	va_list args;
202ce327b62SDarren Tucker 	char *p, prompt[1024];
203ce327b62SDarren Tucker 	int allowed = 0;
204ce327b62SDarren Tucker 
205ce327b62SDarren Tucker 	va_start(args, fmt);
206ce327b62SDarren Tucker 	vsnprintf(prompt, sizeof(prompt), fmt, args);
207ce327b62SDarren Tucker 	va_end(args);
208ce327b62SDarren Tucker 
20959175a35Sdjm@openbsd.org 	p = read_passphrase(prompt,
21059175a35Sdjm@openbsd.org 	    RP_USE_ASKPASS|RP_ALLOW_EOF|RP_ASK_PERMISSION);
211ce327b62SDarren Tucker 	if (p != NULL) {
212ce327b62SDarren Tucker 		/*
213ce327b62SDarren Tucker 		 * Accept empty responses and responses consisting
214ce327b62SDarren Tucker 		 * of the word "yes" as affirmative.
215ce327b62SDarren Tucker 		 */
216ce327b62SDarren Tucker 		if (*p == '\0' || *p == '\n' ||
217ce327b62SDarren Tucker 		    strcasecmp(p, "yes") == 0)
218ce327b62SDarren Tucker 			allowed = 1;
219a627d42eSDarren Tucker 		free(p);
220ce327b62SDarren Tucker 	}
221ce327b62SDarren Tucker 
222ce327b62SDarren Tucker 	return (allowed);
223ce327b62SDarren Tucker }
2245d1c1590Sdjm@openbsd.org 
225d5a0cd4fSdjm@openbsd.org static void
writemsg(const char * msg)226d5a0cd4fSdjm@openbsd.org writemsg(const char *msg)
227d5a0cd4fSdjm@openbsd.org {
228d5a0cd4fSdjm@openbsd.org 	(void)write(STDERR_FILENO, "\r", 1);
229d5a0cd4fSdjm@openbsd.org 	(void)write(STDERR_FILENO, msg, strlen(msg));
230d5a0cd4fSdjm@openbsd.org 	(void)write(STDERR_FILENO, "\r\n", 2);
231d5a0cd4fSdjm@openbsd.org }
232d5a0cd4fSdjm@openbsd.org 
2335d1c1590Sdjm@openbsd.org struct notifier_ctx {
2345d1c1590Sdjm@openbsd.org 	pid_t pid;
2355d1c1590Sdjm@openbsd.org 	void (*osigchld)(int);
2365d1c1590Sdjm@openbsd.org };
2375d1c1590Sdjm@openbsd.org 
2385d1c1590Sdjm@openbsd.org struct notifier_ctx *
notify_start(int force_askpass,const char * fmt,...)2395d1c1590Sdjm@openbsd.org notify_start(int force_askpass, const char *fmt, ...)
2405d1c1590Sdjm@openbsd.org {
2415d1c1590Sdjm@openbsd.org 	va_list args;
2425d1c1590Sdjm@openbsd.org 	char *prompt = NULL;
243d5a0cd4fSdjm@openbsd.org 	pid_t pid = -1;
244d5a0cd4fSdjm@openbsd.org 	void (*osigchld)(int) = NULL;
245e9c20028Sdjm@openbsd.org 	const char *askpass, *s;
246e9c20028Sdjm@openbsd.org 	struct notifier_ctx *ret = NULL;
2475d1c1590Sdjm@openbsd.org 
2485d1c1590Sdjm@openbsd.org 	va_start(args, fmt);
2495d1c1590Sdjm@openbsd.org 	xvasprintf(&prompt, fmt, args);
2505d1c1590Sdjm@openbsd.org 	va_end(args);
2515d1c1590Sdjm@openbsd.org 
2525d1c1590Sdjm@openbsd.org 	if (fflush(NULL) != 0)
253816036f1Sdjm@openbsd.org 		error_f("fflush: %s", strerror(errno));
2545d1c1590Sdjm@openbsd.org 	if (!force_askpass && isatty(STDERR_FILENO)) {
255d5a0cd4fSdjm@openbsd.org 		writemsg(prompt);
256d5a0cd4fSdjm@openbsd.org 		goto out_ctx;
2575d1c1590Sdjm@openbsd.org 	}
258018e2902Sdjm@openbsd.org 	if ((askpass = getenv("SSH_ASKPASS")) == NULL)
259018e2902Sdjm@openbsd.org 		askpass = _PATH_SSH_ASKPASS_DEFAULT;
260e9c20028Sdjm@openbsd.org 	if (*askpass == '\0') {
261816036f1Sdjm@openbsd.org 		debug3_f("cannot notify: no askpass");
262e9c20028Sdjm@openbsd.org 		goto out;
263e9c20028Sdjm@openbsd.org 	}
264e9c20028Sdjm@openbsd.org 	if (getenv("DISPLAY") == NULL &&
265e9c20028Sdjm@openbsd.org 	    ((s = getenv(SSH_ASKPASS_REQUIRE_ENV)) == NULL ||
266e9c20028Sdjm@openbsd.org 	    strcmp(s, "force") != 0)) {
267816036f1Sdjm@openbsd.org 		debug3_f("cannot notify: no display");
268e9c20028Sdjm@openbsd.org 		goto out;
2695d1c1590Sdjm@openbsd.org 	}
2703bf2a6acSdtucker@openbsd.org 	osigchld = ssh_signal(SIGCHLD, SIG_DFL);
2715d1c1590Sdjm@openbsd.org 	if ((pid = fork()) == -1) {
272816036f1Sdjm@openbsd.org 		error_f("fork: %s", strerror(errno));
2733bf2a6acSdtucker@openbsd.org 		ssh_signal(SIGCHLD, osigchld);
2745d1c1590Sdjm@openbsd.org 		free(prompt);
2755d1c1590Sdjm@openbsd.org 		return NULL;
2765d1c1590Sdjm@openbsd.org 	}
2775d1c1590Sdjm@openbsd.org 	if (pid == 0) {
278396d32f3Sdjm@openbsd.org 		if (stdfd_devnull(1, 1, 0) == -1)
279816036f1Sdjm@openbsd.org 			fatal_f("stdfd_devnull failed");
2805d1c1590Sdjm@openbsd.org 		closefrom(STDERR_FILENO + 1);
2815d1c1590Sdjm@openbsd.org 		setenv("SSH_ASKPASS_PROMPT", "none", 1); /* hint to UI */
2825d1c1590Sdjm@openbsd.org 		execlp(askpass, askpass, prompt, (char *)NULL);
283816036f1Sdjm@openbsd.org 		error_f("exec(%s): %s", askpass, strerror(errno));
284f79364baSdjm@openbsd.org 		_exit(1);
2855d1c1590Sdjm@openbsd.org 		/* NOTREACHED */
2865d1c1590Sdjm@openbsd.org 	}
287d5a0cd4fSdjm@openbsd.org  out_ctx:
2885d1c1590Sdjm@openbsd.org 	if ((ret = calloc(1, sizeof(*ret))) == NULL) {
2895d1c1590Sdjm@openbsd.org 		kill(pid, SIGTERM);
290816036f1Sdjm@openbsd.org 		fatal_f("calloc failed");
2915d1c1590Sdjm@openbsd.org 	}
2925d1c1590Sdjm@openbsd.org 	ret->pid = pid;
2935d1c1590Sdjm@openbsd.org 	ret->osigchld = osigchld;
294e9c20028Sdjm@openbsd.org  out:
2955d1c1590Sdjm@openbsd.org 	free(prompt);
2965d1c1590Sdjm@openbsd.org 	return ret;
2975d1c1590Sdjm@openbsd.org }
2985d1c1590Sdjm@openbsd.org 
2995d1c1590Sdjm@openbsd.org void
notify_complete(struct notifier_ctx * ctx,const char * fmt,...)300d5a0cd4fSdjm@openbsd.org notify_complete(struct notifier_ctx *ctx, const char *fmt, ...)
3015d1c1590Sdjm@openbsd.org {
3025d1c1590Sdjm@openbsd.org 	int ret;
303d5a0cd4fSdjm@openbsd.org 	char *msg = NULL;
304d5a0cd4fSdjm@openbsd.org 	va_list args;
305d5a0cd4fSdjm@openbsd.org 
30610dce8ffSdjm@openbsd.org 	if (ctx != NULL && fmt != NULL && ctx->pid == -1) {
307d5a0cd4fSdjm@openbsd.org 		/*
308d5a0cd4fSdjm@openbsd.org 		 * notify_start wrote to stderr, so send conclusion message
309d5a0cd4fSdjm@openbsd.org 		 * there too
310d5a0cd4fSdjm@openbsd.org 		*/
311d5a0cd4fSdjm@openbsd.org 		va_start(args, fmt);
312d5a0cd4fSdjm@openbsd.org 		xvasprintf(&msg, fmt, args);
313d5a0cd4fSdjm@openbsd.org 		va_end(args);
314d5a0cd4fSdjm@openbsd.org 		writemsg(msg);
315*4340dd43Sclaudio@openbsd.org 		free(msg);
316d5a0cd4fSdjm@openbsd.org 	}
3175d1c1590Sdjm@openbsd.org 
3185d1c1590Sdjm@openbsd.org 	if (ctx == NULL || ctx->pid <= 0) {
3195d1c1590Sdjm@openbsd.org 		free(ctx);
3205d1c1590Sdjm@openbsd.org 		return;
3215d1c1590Sdjm@openbsd.org 	}
3225d1c1590Sdjm@openbsd.org 	kill(ctx->pid, SIGTERM);
3235d1c1590Sdjm@openbsd.org 	while ((ret = waitpid(ctx->pid, NULL, 0)) == -1) {
3245d1c1590Sdjm@openbsd.org 		if (errno != EINTR)
3255d1c1590Sdjm@openbsd.org 			break;
3265d1c1590Sdjm@openbsd.org 	}
3275d1c1590Sdjm@openbsd.org 	if (ret == -1)
328816036f1Sdjm@openbsd.org 		fatal_f("waitpid: %s", strerror(errno));
3293bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGCHLD, ctx->osigchld);
3305d1c1590Sdjm@openbsd.org 	free(ctx);
3315d1c1590Sdjm@openbsd.org }
332