ssh borked
authorJann Horn <jann@thejh.net>
Mon, 30 Nov 2015 22:18:06 +0000 (23:18 +0100)
committerJann Horn <jann@thejh.net>
Mon, 30 Nov 2015 22:18:06 +0000 (23:18 +0100)
proxyspace.c [new file with mode: 0644]

diff --git a/proxyspace.c b/proxyspace.c
new file mode 100644 (file)
index 0000000..2b0acc9
--- /dev/null
@@ -0,0 +1,123 @@
+// compile with gcc -o proxyspace proxyspace.c -Wall -std=gnu99
+
+#define _GNU_SOURCE
+
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+#include <sched.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define eprintf(...) fprintf(stderr, __VA_ARGS__)
+
+const struct addrinfo tcp_hints = {
+  .ai_flags = AI_ADDRCONFIG,
+  .ai_family = AF_UNSPEC,
+  .ai_socktype = SOCK_STREAM,
+  .ai_protocol = 0
+};
+
+int netopen(const char *node, const char *service) {
+  struct addrinfo *addrs;
+  if (getaddrinfo(node, service, &tcp_hints, &addrs))
+    errx(1, "unable to resolve address");
+
+  int s = socket(addrs[0].ai_family, addrs[0].ai_socktype, addrs[0].ai_protocol);
+  if (s == -1)
+    err(1, "unable to create socket");
+
+  if (connect(s, addrs[0].ai_addr, addrs[0].ai_addrlen))
+    err(1, "unable to connect");
+
+  freeaddrinfo(addrs);
+  return s;
+}
+
+pid_t fork_nofail(void) {
+  pid_t res = fork();
+  if (res == -1)
+    err(1, "fork failed");
+  return res;
+}
+
+void write_file(char *path, char *data) {
+  int fd = open(path, O_WRONLY);
+  if (fd == -1)
+    err(1, "unable to open \"%s\" for writing", path);
+  ssize_t r = write(fd, data, strlen(data));
+  if (r == -1)
+    err(1, "write to \"%s\" failed", path);
+  if (r != strlen(data))
+    errx(1, "write to \"%s\" failed", path);
+  if (close(fd))
+    err(1, "close failed");
+}
+
+char *xasprintf(char *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  char *ret;
+  if (vasprintf(&ret, fmt, ap) == -1)
+    errx(1, "memory allocation failed");
+  va_end(ap);
+  return ret;
+}
+
+int main(int argc, char **argv) {
+  // invocation: ./proxyspace <sshhost-tcp> <sshport-tcp> <sshtarget-name> <localcommand> [<arg1> ...]
+  char *sshhost = argv[1];
+  char *sshport = argv[2];
+  char *sshtarget = argv[3];
+  char **next_argv = argv+4;
+
+  eprintf("connecting...\n");
+  int netfd = netopen(sshhost, sshport);
+  eprintf("TCP connection successful\n");
+
+  uid_t outer_uid = getuid();
+  gid_t outer_gid = getgid();
+
+  // This is the point of no return: After this, we don't have access to the normal
+  // network anymore.
+  if (unshare(CLONE_NEWUSER|CLONE_NEWNET))
+    err(1, "unable to unshare (maybe unprivileged user namespaces are disabled)");
+
+  // Annoying. Fix up our mapped uid and gid to zero.
+  write_file("/proc/self/setgroups", "deny");
+  write_file("/proc/self/uid_map", xasprintf("0 %d 1\n", outer_uid));
+  write_file("/proc/self/gid_map", xasprintf("0 %d 1\n", outer_gid));
+
+  eprintf("launching ssh...\n");
+  pid_t ssh_pid = fork_nofail();
+  if (ssh_pid == 0) {
+    // -f -n doesn't seem to be working here...
+    execlp("ssh", "ssh", "-w", "0",
+      "-o", xasprintf("ProxyCommand /proc/%d/exe --stage2 %d", (int)getppid(), netfd),
+      sshtarget, NULL);
+    err(1, "execlp for ssh failed");
+  }
+  int ssh_status;
+  pid_t waitres;
+  do {
+    waitres = wait(&ssh_status);
+  } while (waitres != -1 && waitres != ssh_pid);
+  if (waitres == -1)
+    err(1, "unable to wait for ssh???");
+  if (!WIFEXITED(ssh_status))
+    errx(1, "uh... looks like ssh exited in a weird way?");
+  if (WEXITSTATUS(ssh_status) != 0)
+    errx(1, "ssh exited with exit code %d", (int)WEXITSTATUS(ssh_status));
+  eprintf("ssh seems to be connected!\n");
+
+  execvp(next_argv[0], next_argv);
+  err(1, "unable to execute specified next command \"%s\"", next_argv[0]);
+}
\ No newline at end of file