+// 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