From cedd0774183d0e80b048bd76e0d6b850f3d8aa60 Mon Sep 17 00:00:00 2001
From: Martin Pieuchot <mpi@openbsd.org>
Date: Thu, 12 Jul 2012 12:53:44 +0200
Subject: [PATCH] daemon: Support allocating PTYs through openpty on BSD

Allows systems supporting the BSD openpty(3) utily function but not
the Unix98 PTY function family (grantpt(3), unlockpt(3), ...) to
allocate a pseudo-tty required for the ssh authentication.
---
 configure.ac             |    4 ++
 daemon/gvfsbackendsftp.c |    4 +-
 daemon/pty_open.c        |  132 ++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 133 insertions(+), 7 deletions(-)

diff --git a/configure.ac b/configure.ac
index 64a18ef..a05a8f6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -108,9 +108,13 @@ AC_CHECK_FUNCS(getpt posix_openpt grantpt unlockpt ptsname ptsname_r)
 # Pull in the right libraries for various functions which might not be
 # bundled into an exploded libc.
 AC_CHECK_FUNC(socketpair,[have_socketpair=1],AC_CHECK_LIB(socket,socketpair,[have_socketpair=1; LIBS="$LIBS -lsocket"]))
+AC_CHECK_FUNC(openpty,[have_openpty=1],AC_CHECK_LIB(util,openpty,[have_openpty=1; LIBS="$LIBS -lutil"]))
 if test x$have_socketpair = x1 ; then
 	AC_DEFINE(HAVE_SOCKETPAIR,1,[Define if you have the socketpair function.])
 fi
+if test x$have_openpty = x1 ; then
+	AC_DEFINE(HAVE_OPENPTY,1,[Define if you have the openpty function.])
+fi
 
 AC_SEARCH_LIBS(login_tty, util, [AC_DEFINE([HAVE_LOGIN_TTY],[],[Whether login_tty is available])])
 
diff --git a/daemon/gvfsbackendsftp.c b/daemon/gvfsbackendsftp.c
index 4bd44a8..ebfddc0 100644
--- a/daemon/gvfsbackendsftp.c
+++ b/daemon/gvfsbackendsftp.c
@@ -71,8 +71,8 @@
  * fstat
  */
 
-#ifdef HAVE_GRANTPT
-/* We only use this on systems with unix98 ptys */
+#if defined(HAVE_GRANTPT) || defined(HAVE_OPENPTY)
+/* We only use this on systems with unix98 or BSD ptys */
 #define USE_PTY 1
 #endif
 
diff --git a/daemon/pty_open.c b/daemon/pty_open.c
index 22fa6b9..e822189 100644
--- a/daemon/pty_open.c
+++ b/daemon/pty_open.c
@@ -72,6 +72,12 @@
 #include <glib.h>
 #include "pty_open.h"
 
+#if defined(HAVE_PTSNAME_R) || defined(HAVE_PTSNAME) || defined(TIOCGPTN)
+#define HAVE_UNIX98_PTY
+#else
+#undef HAVE_UNIX98_PTY
+#endif
+
 int _pty_set_size(int master, int columns, int rows);
 
 /* Solaris does not have the login_tty() function so implement locally. */
@@ -710,6 +716,8 @@ _pty_unlockpt(int fd)
 #endif
 }
 
+#if defined(HAVE_UNIX98_PTY)
+
 static int
 _pty_open_unix98(pid_t *child, guint flags, char **env_add,
 			   const char *command, char **argv,
@@ -746,6 +754,114 @@ _pty_open_unix98(pid_t *child, guint flags, char **env_add,
 	return fd;
 }
 
+#elif defined(HAVE_OPENPTY)
+
+static int
+_pty_open_bsd(pid_t *child, const char *command, char **argv,
+	      int *stdin_fd, int *stdout_fd, int *stderr_fd)
+{
+	int master, slave;
+	char **args, *arg;
+	int stdin_pipe[2];
+	int stdout_pipe[2];
+	int stderr_pipe[2];
+	pid_t pid;
+	int i;
+
+	if (pipe(stdin_pipe))
+		goto bail_stdin;
+	if (pipe(stdout_pipe))
+		goto bail_stdout;
+	if (pipe(stderr_pipe))
+		goto bail_stderr;
+
+	if (openpty(&master, &slave, NULL, NULL, NULL) == -1)
+		return (-1);
+
+	switch(pid = fork()) {
+	case -1:
+		goto bail_fork;
+	case 0:
+		/*
+		 * Child
+		 */
+		close(master);
+		close(stdin_pipe[1]);
+		close(stdout_pipe[0]);
+		close(stderr_pipe[0]);
+
+		setsid();
+		if (ioctl(slave, TIOCSCTTY, (char *)NULL) == -1)
+			_exit(0);
+
+		/* Set up stdin/out/err */
+		dup2(stdin_pipe[0], STDIN_FILENO);
+		dup2(stdout_pipe[1], STDOUT_FILENO);
+		dup2(stderr_pipe[1], STDERR_FILENO);
+		close(stdin_pipe[0]);
+		close(stdout_pipe[1]);
+		close(stderr_pipe[1]);
+
+		/* Reset our signals -- our parent may have done any number of
+		 * weird things to them. */
+		_pty_reset_signal_handlers();
+
+		/* Outta here. */
+		if (argv != NULL) {
+			for (i = 0; (argv[i] != NULL); i++) ;
+			args = g_malloc0(sizeof(char*) * (i + 1));
+			for (i = 0; (argv[i] != NULL); i++) {
+				args[i] = g_strdup(argv[i]);
+			}
+			execvp(command, args);
+		} else {
+			arg = g_strdup(command);
+			execlp(command, arg, NULL);
+		}
+
+		/* Avoid calling any atexit() code. */
+		_exit(0);
+		g_assert_not_reached();
+	}
+
+	/*
+	 * Parent
+	 */
+
+	/* XXX Don't close the slave pty, it's now the control
+	 *     terminal of the child and ssh needs it to authenticate.
+	close(slave);
+	 */
+	close(stdin_pipe[0]);
+	close(stdout_pipe[1]);
+	close(stderr_pipe[1]);
+
+	*child = pid;
+	*stdin_fd = stdin_pipe[1];
+	*stdout_fd = stdout_pipe[0];
+	*stderr_fd = stderr_pipe[0];
+
+	return (master);
+
+ bail_fork:
+	close(stderr_pipe[0]);
+	close(stderr_pipe[1]);
+ bail_stderr:
+	close(stdout_pipe[0]);
+	close(stdout_pipe[1]);
+ bail_stdout:
+	close(stdin_pipe[0]);
+	close(stdin_pipe[1]);
+ bail_stdin:
+
+	*child = -1;
+	return -1;
+}
+
+#else
+#error Have neither UNIX98 PTY nor BSD openpty!
+#endif /* HAVE_UNIX98_PTY */
+
 /**
  * pty_open:
  * @child: location to store the new process's ID
@@ -774,11 +890,17 @@ pty_open(pid_t *child, guint flags, char **env_add,
 	 int *stdin_fd, int *stdout_fd, int *stderr_fd)
 {
 	int ret = -1;
-	if (ret == -1) {
-		ret = _pty_open_unix98(child, flags, env_add, command, 
-						 argv, directory, columns, rows,
-						 stdin_fd, stdout_fd, stderr_fd);
-	}
+
+#if defined(HAVE_UNIX98_PTY)
+	ret = _pty_open_unix98(child, flags, env_add, command, argv, directory,
+			       columns, rows, stdin_fd, stdout_fd, stderr_fd);
+#elif defined(HAVE_OPENPTY)
+	ret = _pty_open_bsd(child, command, argv,
+			    stdin_fd, stdout_fd, stderr_fd);
+#else
+#error Have neither UNIX98 PTY nor BSD openpty!
+#endif
+
 	return ret;
 }
 
-- 
1.7.6

