qrexec: use sockets instead of pipes to communicate with child process
The main advantage is possible use of single socket for both stdin and stdout. This is strictly required for using USBIP over qrexec. For compatibility qrexec still creates three socket pairs (instead of pipes) for stdin/out/err respectively. When qrexec-agent receives SIGUSR1, it will close stdout socket and use stdin socket for both directions. Some additional work is needed here to actually allow child process to send that signal - qrexec is running as root, but child as "user" in most cases.
This commit is contained in:
parent
b40c791914
commit
c1cb78e0e8
@ -29,6 +29,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
#include <libvchan.h>
|
||||
#include "qrexec.h"
|
||||
@ -38,6 +39,7 @@
|
||||
#define VCHAN_BUFFER_SIZE 65536
|
||||
|
||||
static volatile int child_exited;
|
||||
static volatile int stdio_socket_requested;
|
||||
int stdout_msg_type = MSG_DATA_STDOUT;
|
||||
pid_t child_process_pid;
|
||||
|
||||
@ -47,6 +49,12 @@ static void sigchld_handler(int __attribute__((__unused__))x)
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
}
|
||||
|
||||
static void sigusr1_handler(int __attribute__((__unused__))x)
|
||||
{
|
||||
stdio_socket_requested = 1;
|
||||
signal(SIGUSR1, SIG_IGN);
|
||||
}
|
||||
|
||||
|
||||
void no_colon_in_cmd()
|
||||
{
|
||||
@ -146,7 +154,10 @@ int handle_input(libvchan_t *vchan, int fd, int msg_type)
|
||||
return -1;
|
||||
|
||||
if (len == 0) {
|
||||
close(fd);
|
||||
if (shutdown(fd, SHUT_RD) < 0) {
|
||||
if (errno == ENOTSOCK)
|
||||
close(fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -188,14 +199,20 @@ int handle_remote_data(libvchan_t *data_vchan, int stdin_fd)
|
||||
/* discard the data */
|
||||
continue;
|
||||
if (hdr.len == 0) {
|
||||
close(stdin_fd);
|
||||
if (shutdown(stdin_fd, SHUT_WR) < 0) {
|
||||
if (errno == ENOTSOCK)
|
||||
close(stdin_fd);
|
||||
}
|
||||
stdin_fd = -1;
|
||||
return 0;
|
||||
} else {
|
||||
/* FIXME: use buffered write here to prevent deadlock */
|
||||
if (!write_all(stdin_fd, buf, hdr.len)) {
|
||||
if (errno == EPIPE) {
|
||||
close(stdin_fd);
|
||||
if (errno == EPIPE || errno == ECONNRESET) {
|
||||
if (shutdown(stdin_fd, SHUT_WR) < 0) {
|
||||
if (errno == ENOTSOCK)
|
||||
close(stdin_fd);
|
||||
}
|
||||
stdin_fd = -1;
|
||||
} else {
|
||||
perror("write");
|
||||
@ -248,7 +265,10 @@ void process_child_io(libvchan_t *data_vchan,
|
||||
if (pid == child_process_pid) {
|
||||
child_process_status = WEXITSTATUS(status);
|
||||
if (stdin_fd >= 0) {
|
||||
close(stdin_fd);
|
||||
if (shutdown(stdin_fd, SHUT_WR) < 0) {
|
||||
if (errno == ENOTSOCK)
|
||||
close(stdin_fd);
|
||||
}
|
||||
stdin_fd = -1;
|
||||
}
|
||||
}
|
||||
@ -264,6 +284,13 @@ void process_child_io(libvchan_t *data_vchan,
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* child signaled desire to use single socket for both stdin and stdout */
|
||||
if (stdio_socket_requested) {
|
||||
if (stdout_fd != -1)
|
||||
close(stdout_fd);
|
||||
stdout_fd = stdin_fd;
|
||||
stdio_socket_requested = 0;
|
||||
}
|
||||
/* otherwise handle the events */
|
||||
|
||||
FD_ZERO(&rdset);
|
||||
@ -337,7 +364,10 @@ void process_child_io(libvchan_t *data_vchan,
|
||||
break;
|
||||
case -2:
|
||||
/* remote process exited, no sense in sending more data to it */
|
||||
close(stdout_fd);
|
||||
if (shutdown(stdout_fd, SHUT_RD) < 0) {
|
||||
if (errno == ENOTSOCK)
|
||||
close(stdout_fd);
|
||||
}
|
||||
stdout_fd = -1;
|
||||
close(stderr_fd);
|
||||
stderr_fd = -1;
|
||||
@ -353,6 +383,7 @@ pid_t handle_new_process(int type, int connect_domain, int connect_port,
|
||||
libvchan_t *data_vchan;
|
||||
pid_t pid;
|
||||
int stdin_fd, stdout_fd, stderr_fd;
|
||||
char pid_s[10];
|
||||
|
||||
if (type == MSG_SERVICE_CONNECT) {
|
||||
if (cmdline_len != sizeof(*svc_params)) {
|
||||
@ -394,6 +425,10 @@ pid_t handle_new_process(int type, int connect_domain, int connect_port,
|
||||
handle_handshake(data_vchan);
|
||||
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
signal(SIGUSR1, sigusr1_handler);
|
||||
snprintf(pid_s, sizeof(pid_s), "%d", getpid());
|
||||
setenv("QREXEC_AGENT_PID", pid_s, 1);
|
||||
/* TODO: use setresuid to allow child process to actually send the signal? */
|
||||
|
||||
switch (type) {
|
||||
case MSG_JUST_EXEC:
|
||||
|
Loading…
Reference in New Issue
Block a user