Get sshd working well enough for shell and sftp sessions.

The main thing that doesn't work is terminating connections when the
command (sh or sftp-server) terminates since we don't have signals.

Change-Id: Ic4a2e355f3b1c54ad3990b124d9d49cf6e7a6f15
diff --git a/BUILD.gn b/BUILD.gn
index 65500c7..26caaf7 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -238,6 +238,7 @@
     ":umac128",
     "//third_party/boringssl",
   ]
+  libs = [ "launchpad" ]
   configs += [ ":fuchsia" ]
 }
 
diff --git a/auth.c b/auth.c
index 6ee6116..27797df 100644
--- a/auth.c
+++ b/auth.c
@@ -174,6 +174,7 @@
 			free(shell);
 			return 0;
 		}
+#ifndef __Fuchsia__
 		if (S_ISREG(st.st_mode) == 0 ||
 		    (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
 			logit("User %.100s not allowed because shell %.100s "
@@ -181,6 +182,7 @@
 			free(shell);
 			return 0;
 		}
+#endif  // __Fuchsia__
 		free(shell);
 	}
 
diff --git a/fuchsia/README.md b/fuchsia/README.md
new file mode 100644
index 0000000..aa1a6d7
--- /dev/null
+++ b/fuchsia/README.md
@@ -0,0 +1,29 @@
+# OpenSSH on Fuchsia
+
+## Running
+
+### ssh
+
+### sshd
+
+First generate host keys on your Fuchsia device:
+```
+$ ssh-keygen -A
+```
+
+Then copy your public SSH key to your Fuchsia device from your host:
+```
+% netcp ~/.ssh/id_rsa.pub :/.ssh/authorized_keys
+```
+
+Run `sshd` with `-d` for debug mode and `-r` to disable reexecing:
+```
+$ sshd -dr
+```
+### scp
+
+### sftp
+
+## Port Notes
+
+TODO
diff --git a/fuchsia/config.h b/fuchsia/config.h
index 802c4ba..bd98195 100644
--- a/fuchsia/config.h
+++ b/fuchsia/config.h
@@ -961,7 +961,7 @@
 /* #undef HAVE_SETGROUPENT */
 
 /* Define to 1 if you have the `setgroups' function. */
-#define HAVE_SETGROUPS 1
+/*#define HAVE_SETGROUPS 1*/
 
 /* Define to 1 if you have the `setlinebuf' function. */
 #define HAVE_SETLINEBUF 1
@@ -1045,7 +1045,7 @@
 #define HAVE_SNPRINTF 1
 
 /* Define to 1 if you have the `socketpair' function. */
-#define HAVE_SOCKETPAIR 1
+/*#define HAVE_SOCKETPAIR 1*/
 
 /* Have PEERCRED socket option */
 #define HAVE_SO_PEERCRED 1
@@ -1162,7 +1162,7 @@
 /* #undef HAVE_SWAP32 */
 
 /* Define to 1 if you have the `sysconf' function. */
-#define HAVE_SYSCONF 1
+/*#define HAVE_SYSCONF 1*/
 
 /* Define if you have syslen in utmpx.h */
 /* #undef HAVE_SYSLEN_IN_UTMPX */
@@ -1678,7 +1678,7 @@
 /* #undef USE_PAM */
 
 /* Use PIPES instead of a socketpair() */
-/* #undef USE_PIPES */
+#define USE_PIPES 1
 
 /* Define if you have Solaris privileges */
 /* #undef USE_SOLARIS_PRIVS */
diff --git a/fuchsia/fuchsia-compat.c b/fuchsia/fuchsia-compat.c
index 0fa72de..efabdaa 100644
--- a/fuchsia/fuchsia-compat.c
+++ b/fuchsia/fuchsia-compat.c
@@ -1,5 +1,14 @@
+// Copyright 2017 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <launchpad/launchpad.h>
 #include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 int chroot(const char *path) { return -1; }
 
@@ -31,3 +40,38 @@
 struct passwd *getpwuid(uid_t uid) {
   return getpwent();
 }
+
+#define ARGV_MAX 256
+
+mx_handle_t fuchsia_launch_child(const char *command, int in, int out, int err) {
+  const char *argv[ARGV_MAX];
+  int argc = 1;
+  argv[0] = "/boot/bin/sh";
+  if (command) {
+    argv[argc++] = "-c";
+    argv[argc++] = command;
+  } else {
+    command = argv[0];
+  }
+  argv[argc] = NULL;
+
+  launchpad_t *lp;
+  launchpad_create(0, command, &lp);
+  launchpad_load_from_file(lp, argv[0]);
+  launchpad_set_args(lp, argc, argv);
+  launchpad_clone(lp, LP_CLONE_MXIO_ROOT|LP_CLONE_MXIO_CWD);
+  // TODO: set up environment
+  launchpad_transfer_fd(lp, in, STDIN_FILENO);
+  launchpad_transfer_fd(lp, out, STDOUT_FILENO);
+  launchpad_transfer_fd(lp, err, STDERR_FILENO);
+
+  mx_handle_t proc = 0;
+  const char* errmsg;
+
+  mx_status_t status = launchpad_go(lp, &proc, &errmsg);
+  if (status < 0) {
+    printf("error from launchpad_go: %s\n", errmsg);
+  }
+
+  return proc;
+}
diff --git a/fuchsia/fuchsia-compat.h b/fuchsia/fuchsia-compat.h
index db749d6..a04794d 100644
--- a/fuchsia/fuchsia-compat.h
+++ b/fuchsia/fuchsia-compat.h
@@ -1,5 +1,12 @@
+// Copyright 2017 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
 #pragma once
+#include <magenta/types.h>
+
 int chroot(const char *path);
+mx_handle_t fuchsia_launch_child(const char *command, int in, int out, int err);
 
 #define CUSTOM_SYS_AUTH_PASSWD
 
diff --git a/fuchsia/sshd_config b/fuchsia/sshd_config
index 08ab390..5b94765 100644
--- a/fuchsia/sshd_config
+++ b/fuchsia/sshd_config
@@ -93,7 +93,7 @@
 #PrintLastLog yes
 #TCPKeepAlive yes
 #UseLogin no
-#UsePrivilegeSeparation sandbox
+UsePrivilegeSeparation no
 #PermitUserEnvironment no
 #Compression delayed
 #ClientAliveInterval 0
@@ -109,7 +109,7 @@
 #Banner none
 
 # override default of no subsystems
-Subsystem	sftp	/usr/libexec/sftp-server
+Subsystem	sftp	/system/bin/sftp-server
 
 # Example of overriding settings on a per-user basis
 #Match User anoncvs
diff --git a/openbsd-compat/bsd-closefrom.c b/openbsd-compat/bsd-closefrom.c
index 9380b33..a7aa4b3 100644
--- a/openbsd-compat/bsd-closefrom.c
+++ b/openbsd-compat/bsd-closefrom.c
@@ -47,6 +47,10 @@
 # endif
 #endif
 
+#ifdef __Fuchsia__
+#include <mxio/limits.h>
+#endif
+
 #ifndef OPEN_MAX
 # define OPEN_MAX	256
 #endif
@@ -95,6 +99,8 @@
 	 */
 #ifdef HAVE_SYSCONF
 	maxfd = sysconf(_SC_OPEN_MAX);
+#elif __Fuchsia__
+        maxfd = MAX_MXIO_FD;
 #else
 	maxfd = getdtablesize();
 #endif /* HAVE_SYSCONF */
diff --git a/session.c b/session.c
index a08aa69..c51ede8 100644
--- a/session.c
+++ b/session.c
@@ -343,6 +343,27 @@
 
 	session_proctitle(s);
 
+#ifdef __Fuchsia__
+        mx_handle_t process_handle = fuchsia_launch_child(command, pin[0], pout[1], perr[1]);
+        if (process_handle == 0) {
+#ifdef USE_PIPES
+		close(pin[0]);
+		close(pin[1]);
+		close(pout[0]);
+		close(pout[1]);
+		close(perr[0]);
+		close(perr[1]);
+#else
+		close(inout[0]);
+		close(inout[1]);
+		close(err[0]);
+		close(err[1]);
+#endif
+		return -1;
+        }
+        // Force an mx_handle_t into a pid_t.
+        pid = (pid_t)process_handle;
+#else
 	/* Fork the child. */
 	switch ((pid = fork())) {
 	case -1:
@@ -434,6 +455,7 @@
 #endif
 
 	s->pid = pid;
+#endif  // __Fuchsia__
 	/* Set interactive/non-interactive mode. */
 	packet_set_interactive(s->display != NULL,
 	    options.ip_qos_interactive, options.ip_qos_bulk);