segfault/host/sf-sshd.patch
SkyperTHC ecbe90763f
more
2023-02-19 17:15:42 +00:00

200 lines
5.8 KiB
Diff

diff -x !*.[ch] -u openssh-9.1p1-orig/channels.c openssh-9.1p1-sf/channels.c
--- openssh-9.1p1-orig/channels.c 2022-10-03 15:51:42
+++ openssh-9.1p1-sf/channels.c 2023-01-29 21:13:05
@@ -3510,7 +3510,7 @@
ssh->chanctxt->IPv4or6 = af;
}
-
+extern int sf_by_signal;
/*
* Determine whether or not a port forward listens to loopback, the
* specified address or wildcard. On the client, a specified bind
@@ -3548,6 +3548,7 @@
* address and it was overridden.
*/
if (*listen_addr != '\0' &&
+ sf_by_signal == 0 &&
strcmp(listen_addr, "0.0.0.0") != 0 &&
strcmp(listen_addr, "*") != 0) {
ssh_packet_send_debug(ssh,
diff -x !*.[ch] -u openssh-9.1p1-orig/serverloop.c openssh-9.1p1-sf/serverloop.c
--- openssh-9.1p1-orig/serverloop.c 2022-10-03 15:51:42
+++ openssh-9.1p1-sf/serverloop.c 2023-01-29 21:39:06
@@ -102,6 +102,12 @@
/* requested tunnel forwarding interface(s), shared with session.c */
char *tun_fwd_ifnames = NULL;
+extern int sf_done;
+extern size_t sf_ports_n;
+extern int sf_ports[64];
+extern int sf_sigusr1_received;
+void sf_sshd2ns(void);
+
/* returns 1 if bind to specified port by specified user is permitted */
static int
bind_permitted(int port, uid_t uid)
@@ -380,6 +386,8 @@
if (sigprocmask(SIG_BLOCK, &bsigset, &osigset) == -1)
error_f("bsigset sigprocmask: %s", strerror(errno));
collect_children(ssh);
+ if (sf_sigusr1_received != 0)
+ sf_sshd2ns();
wait_until_can_do_something(ssh, connection_in, connection_out,
&pfd, &npfd_alloc, &npfd_active, rekey_timeout_ms, &osigset,
&conn_in_ready, &conn_out_ready);
@@ -637,12 +645,14 @@
if (strcmp(ctype, "session") == 0) {
c = server_request_session(ssh);
- } else if (strcmp(ctype, "direct-tcpip") == 0) {
- c = server_request_direct_tcpip(ssh, &reason, &errmsg);
- } else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) {
- c = server_request_direct_streamlocal(ssh);
- } else if (strcmp(ctype, "tun@openssh.com") == 0) {
- c = server_request_tun(ssh);
+ } else if (sf_done != 0) {
+ if (strcmp(ctype, "direct-tcpip") == 0) {
+ c = server_request_direct_tcpip(ssh, &reason, &errmsg);
+ } else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) {
+ c = server_request_direct_streamlocal(ssh);
+ } else if (strcmp(ctype, "tun@openssh.com") == 0) {
+ c = server_request_tun(ssh);
+ }
}
if (c != NULL) {
debug_f("confirm %s", ctype);
@@ -802,8 +812,20 @@
ssh_packet_send_debug(ssh, "Server has disabled port forwarding.");
} else {
/* Start listening on the port */
- success = channel_setup_remote_fwd_listener(ssh, &fwd,
- &allocated_listen_port, &options.fwd_opts);
+ if (sf_done == 0)
+ {
+ // HERE: sshd has not yet been moved to guest's network namespace.
+ // Fake the -R request and complete in cb_sigusr1().
+ allocated_listen_port = fwd.listen_port;
+ if (sf_ports_n < sizeof sf_ports / sizeof *sf_ports)
+ {
+ success = 1;
+ sf_ports[sf_ports_n++] = fwd.listen_port;
+ }
+ } else {
+ success = channel_setup_remote_fwd_listener(ssh, &fwd,
+ &allocated_listen_port, &options.fwd_opts);
+ }
}
if ((resp = sshbuf_new()) == NULL)
fatal_f("sshbuf_new");
diff -x !*.[ch] -u openssh-9.1p1-orig/sshd.c openssh-9.1p1-sf/sshd.c
--- openssh-9.1p1-orig/sshd.c 2022-10-03 15:51:42
+++ openssh-9.1p1-sf/sshd.c 2023-01-29 21:13:05
@@ -536,8 +536,71 @@
return 0;
}
}
+#include <linux/types.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#ifndef SECBIT_KEEP_CAPS
+#define SECBIT_KEEP_CAPS (1<<4)
+#endif
+int sf_done;
+int sf_by_signal;
+int sf_sigusr1_received;
+int sf_ports[64];
+size_t sf_ports_n;
+static char sf_nsnet_name[128];
+static struct ssh *sf_ssh;
static void
+cb_sigusr1(int sig)
+{
+ debug("SIGUSR1 RECEIVED");
+ sf_sigusr1_received = 1;
+ signal(SIGUSR1, SIG_IGN);
+}
+
+void
+sf_sshd2ns(void)
+{
+ int fd;
+ if ((fd = open(sf_nsnet_name, O_RDONLY | O_CLOEXEC)) < 0)
+ {
+ debug("open(%s)=%d: %s", sf_nsnet_name, fd, strerror(errno));
+ exit(252);
+ }
+
+ sf_sigusr1_received = 0;
+
+ // No longer needed
+ unlink(sf_nsnet_name);
+
+ debug("THC moving sshd. setns(%s)", sf_nsnet_name);
+ if (setns(fd, CLONE_NEWNET) != 0)
+ {
+ debug("THC setns(%s) (fd=%d): %s", sf_nsnet_name, fd, strerror(errno));
+ exit(255);
+ }
+
+ // Continue -R forwards
+ if (sf_ssh == NULL)
+ return;
+ sf_by_signal = 1;
+ struct Forward fwd;
+ memset(&fwd, 0, sizeof fwd);
+ fwd.listen_host = "localhost";
+ size_t i;
+ for (i = 0; i < sf_ports_n; i++)
+ {
+ fwd.listen_port = sf_ports[i];
+ debug("THC remote forward #%zu for %d", i, fwd.listen_port);
+ channel_setup_remote_fwd_listener(sf_ssh, &fwd, NULL /* allocated_listen_port */, &options.fwd_opts);
+ }
+ sf_ports_n = 0;
+ sf_by_signal = 0;
+
+ sf_done = 1;
+}
+
+static void
privsep_postauth(struct ssh *ssh, Authctxt *authctxt)
{
#ifdef DISABLE_FD_PASSING
@@ -576,8 +639,34 @@
reseed_prngs();
+ // Keep CAPS after setuid() so that non priv can call setns() with CAP_SYS_ADMIN
+ prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS);
+
/* Drop privileges */
do_setusercontext(authctxt->pw);
+
+ // Set the effective CAPS to remove SECUREBITS
+ cap_t caps = cap_get_proc();
+ const cap_value_t cl[] = {CAP_SETPCAP};
+ cap_set_flag(caps, CAP_EFFECTIVE, 1, cl, CAP_SET);
+ cap_set_proc(caps);
+
+ // Delete SECBIT_KEEP_CAPS
+ if (prctl(PR_SET_SECUREBITS, 0) != 0)
+ exit(254);
+
+ // Only keep CAP_SYS_ADMIN for setns()
+ const cap_value_t cap_list[1] = {CAP_SYS_ADMIN};
+ cap_clear(caps);
+ cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET);
+ cap_set_flag(caps, CAP_PERMITTED, 1, cap_list, CAP_SET);
+ cap_set_proc(caps);
+ cap_free(caps);
+
+ // segfaultsh will signal with USR1 when guest's PID is known (for setns()).
+ snprintf(sf_nsnet_name, sizeof sf_nsnet_name, "/dev/shm/ns-net-%d", getpid());
+ sf_ssh = ssh;
+ signal(SIGUSR1, cb_sigusr1);
skip:
/* It is safe now to apply the key state */