From: Jann Horn Date: Mon, 30 Nov 2015 22:18:06 +0000 (+0100) Subject: ssh borked X-Git-Url: http://git.thejh.net/?p=proxyspace.git;a=commitdiff_plain;h=18d44df1ae086cc2c1015d170f73728fac322beb;ds=sidebyside ssh borked --- 18d44df1ae086cc2c1015d170f73728fac322beb diff --git a/proxyspace.c b/proxyspace.c new file mode 100644 index 0000000..2b0acc9 --- /dev/null +++ b/proxyspace.c @@ -0,0 +1,123 @@ +// compile with gcc -o proxyspace proxyspace.c -Wall -std=gnu99 + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 [ ...] + 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