From 15c55a4ef5660f473ce96e023a38e43cf7a22fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Mon, 8 Apr 2019 15:53:52 +0200 Subject: [PATCH] Remove qrexec related files Move them to the core-qrexec repository. QubesOS/qubes-issues#4955 --- qrexec/Makefile | 12 - qrexec/README.rpc | 64 --- qrexec/qrexec-client.c | 818 ----------------------------- qrexec/qrexec-daemon.c | 874 ------------------------------- qrexec/qubes-rpc-multiplexer | 45 -- rpm_spec/core-dom0-linux.spec.in | 19 +- 6 files changed, 1 insertion(+), 1831 deletions(-) delete mode 100644 qrexec/Makefile delete mode 100644 qrexec/README.rpc delete mode 100644 qrexec/qrexec-client.c delete mode 100644 qrexec/qrexec-daemon.c delete mode 100755 qrexec/qubes-rpc-multiplexer diff --git a/qrexec/Makefile b/qrexec/Makefile deleted file mode 100644 index 5a24850..0000000 --- a/qrexec/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -CC=gcc -CFLAGS+=-I. -g -O2 -Wall -Wextra -Werror -pie -fPIC `pkg-config --cflags vchan-$(BACKEND_VMM)` -LIBS=`pkg-config --libs vchan-$(BACKEND_VMM)` -lqrexec-utils - - -all: qrexec-daemon qrexec-client -qrexec-daemon: qrexec-daemon.o - $(CC) -pie -g -o qrexec-daemon qrexec-daemon.o $(LIBS) -qrexec-client: qrexec-client.o - $(CC) -pie -g -o qrexec-client qrexec-client.o $(LIBS) -clean: - rm -f *.o *~ qrexec-daemon qrexec-client diff --git a/qrexec/README.rpc b/qrexec/README.rpc deleted file mode 100644 index 63af300..0000000 --- a/qrexec/README.rpc +++ /dev/null @@ -1,64 +0,0 @@ - Currently (after commit 2600134e3bb781fca25fe77e464f8b875741dc83), -qrexec_agent can request a service (specified by a "exec_index") to be -executed on a different VM or dom0. Access control is enforced in dom0 via -files in /etc/qubes_rpc/policy. File copy, Open in Dispvm, sync appmenus, -upload updates to dom0 - they all have been ported to the new API. -See the quick HOWTO section on how to add a new service. Note we have -qvm-open-in-vm utility practically for free. - -CHANGES - - Besides flexibility offered by /etc/qubes_rpc/policy, writing a client -is much simpler now. The workflow used to be (using "filecopy" service as -an example): -a) "filecopy_ui" process places job description in some spool directory, -signals qrexec_agent to signal qrexec_daemon -b) qrexec_daemon executes "qrexec_client -d domain filecopy_worker ...." -and "filecopy_worker" process needed to parse spool and retrieve job -description from there. Particularly, "filecopy_ui" had no connection to -remote. - Now, the flow is: -a) qrexec_client_vm process obtains 3 unix socket descriptors from -qrexec_agent, dup stdin/out/err to them; forms "existing_process_handle" from -them -b) qrexec_client_vm signals qrexec_agent to signal qrexec_daemon, with a -"exec_index" (so, type of service) as an argument -c) qrexec_daemon executed "qrexec_client -d domain -c existing_process_handle ...." -d) qrexec_client_vm execve filecopy_program. - -Thus, there is only one service program, and it has direct access to remote via -stdin/stdout. - -HOWTO - -Let's add a new "test.Add" service, that will add two numbers. We need the -following files in the template fs: -========================== -/usr/bin/our_test_add_client: -#!/bin/sh -echo $1 $2 -exec cat >&2 -# more correct: exec cat >&$SAVED_FD_1, but do not scare the reader -========================== -/usr/bin/our_test_add_server: -#!/bin/sh -read arg1 arg2 -echo $(($arg1+$arg2)) -========================== -/etc/qubes_rpc/test.Add: -/usr/bin/our_test_add_server - -Now, on the client side, we start the client via -/usr/lib/qubes/qrexec_client_vm target_vm test.Add /usr/bin/our_test_add_client 11 22 - -Because there is no policy yet, dom0 will ask you to create one (of cource you -can do it before the first run of our_test_add_client). So, in dom0, create (by now, -with a file editor) the /etc/qubes_rpc/policy/test.Add file with -anyvm anyvm ask -content. The format of the /etc/qubes_rpc/policy/* files is -srcvm destvm (allow|deny|ask)[,user=user_to_run_as][,target=VM_to_redirect_to] - -You can specify srcvm and destvm by name, or by one of "anyvm", "dispvm", "dom0" -reserved keywords. -Then, when you confirm the operation, you will get the result in the client vm. - diff --git a/qrexec/qrexec-client.c b/qrexec/qrexec-client.c deleted file mode 100644 index 8b8d5a6..0000000 --- a/qrexec/qrexec-client.c +++ /dev/null @@ -1,818 +0,0 @@ -/* - * The Qubes OS Project, http://www.qubes-os.org - * - * Copyright (C) 2010 Rafal Wojtczuk - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "qrexec.h" -#include "libqrexec-utils.h" - -// whether qrexec-client should replace problematic bytes with _ before printing the output -int replace_chars_stdout = 0; -int replace_chars_stderr = 0; - -#define VCHAN_BUFFER_SIZE 65536 - -int local_stdin_fd, local_stdout_fd; -pid_t local_pid = 0; -/* flag if this is "remote" end of service call. In this case swap STDIN/STDOUT - * msg types and send exit code at the end */ -int is_service = 0; -int child_exited = 0; - -extern char **environ; - -static int handle_agent_handshake(libvchan_t *vchan, int remote_send_first) -{ - struct msg_header hdr; - struct peer_info info; - int who = 0; // even - send to remote, odd - receive from remote - - while (who < 2) { - if ((who+remote_send_first) & 1) { - if (!read_vchan_all(vchan, &hdr, sizeof(hdr))) { - perror("daemon handshake"); - return -1; - } - if (hdr.type != MSG_HELLO || hdr.len != sizeof(info)) { - fprintf(stderr, "Invalid daemon MSG_HELLO\n"); - return -1; - } - if (!read_vchan_all(vchan, &info, sizeof(info))) { - perror("daemon handshake"); - return -1; - } - - if (info.version != QREXEC_PROTOCOL_VERSION) { - fprintf(stderr, "Incompatible daemon protocol version " - "(daemon %d, client %d)\n", - info.version, QREXEC_PROTOCOL_VERSION); - return -1; - } - } else { - hdr.type = MSG_HELLO; - hdr.len = sizeof(info); - info.version = QREXEC_PROTOCOL_VERSION; - - if (!write_vchan_all(vchan, &hdr, sizeof(hdr))) { - fprintf(stderr, "Failed to send MSG_HELLO hdr to daemon\n"); - return -1; - } - if (!write_vchan_all(vchan, &info, sizeof(info))) { - fprintf(stderr, "Failed to send MSG_HELLO to daemon\n"); - return -1; - } - } - who++; - } - return 0; -} - -static int handle_daemon_handshake(int fd) -{ - struct msg_header hdr; - struct peer_info info; - - /* daemon send MSG_HELLO first */ - if (!read_all(fd, &hdr, sizeof(hdr))) { - perror("daemon handshake"); - return -1; - } - if (hdr.type != MSG_HELLO || hdr.len != sizeof(info)) { - fprintf(stderr, "Invalid daemon MSG_HELLO\n"); - return -1; - } - if (!read_all(fd, &info, sizeof(info))) { - perror("daemon handshake"); - return -1; - } - - if (info.version != QREXEC_PROTOCOL_VERSION) { - fprintf(stderr, "Incompatible daemon protocol version " - "(daemon %d, client %d)\n", - info.version, QREXEC_PROTOCOL_VERSION); - return -1; - } - - hdr.type = MSG_HELLO; - hdr.len = sizeof(info); - info.version = QREXEC_PROTOCOL_VERSION; - - if (!write_all(fd, &hdr, sizeof(hdr))) { - fprintf(stderr, "Failed to send MSG_HELLO hdr to daemon\n"); - return -1; - } - if (!write_all(fd, &info, sizeof(info))) { - fprintf(stderr, "Failed to send MSG_HELLO to daemon\n"); - return -1; - } - return 0; -} - -static int connect_unix_socket(const char *domname) -{ - int s, len; - struct sockaddr_un remote; - - if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - perror("socket"); - return -1; - } - - remote.sun_family = AF_UNIX; - snprintf(remote.sun_path, sizeof remote.sun_path, - QREXEC_DAEMON_SOCKET_DIR "/qrexec.%s", domname); - len = strlen(remote.sun_path) + sizeof(remote.sun_family); - if (connect(s, (struct sockaddr *) &remote, len) == -1) { - perror("connect"); - exit(1); - } - if (handle_daemon_handshake(s) < 0) - exit(1); - return s; -} - -static void sigchld_handler(int x __attribute__((__unused__))) -{ - child_exited = 1; - signal(SIGCHLD, sigchld_handler); -} - -/* called from do_fork_exec */ -_Noreturn void do_exec(char *prog) -{ - /* avoid calling qubes-rpc-multiplexer through shell */ - exec_qubes_rpc_if_requested(prog, environ); - - /* if above haven't executed qubes-rpc-multiplexer, pass it to shell */ - execl("/bin/bash", "bash", "-c", prog, NULL); - perror("exec bash"); - exit(1); -} - -static void do_exit(int code) -{ - int status; - /* restore flags, as we may have not the only copy of this file descriptor - */ - if (local_stdin_fd != -1) - set_block(local_stdin_fd); - close(local_stdin_fd); - close(local_stdout_fd); - // sever communication lines; wait for child, if any - // so that qrexec-daemon can count (recursively) spawned processes correctly - waitpid(-1, &status, 0); - exit(code); -} - - -static void prepare_local_fds(char *cmdline) -{ - if (!cmdline) { - local_stdin_fd = 1; - local_stdout_fd = 0; - return; - } - signal(SIGCHLD, sigchld_handler); - do_fork_exec(cmdline, &local_pid, &local_stdin_fd, &local_stdout_fd, - NULL); -} - -/* ask the daemon to allocate vchan port */ -static void negotiate_connection_params(int s, int other_domid, unsigned type, - void *cmdline_param, int cmdline_size, - int *data_domain, int *data_port) -{ - struct msg_header hdr; - struct exec_params params; - hdr.type = type; - hdr.len = sizeof(params) + cmdline_size; - params.connect_domain = other_domid; - params.connect_port = 0; - if (!write_all(s, &hdr, sizeof(hdr)) - || !write_all(s, ¶ms, sizeof(params)) - || !write_all(s, cmdline_param, cmdline_size)) { - perror("write daemon"); - do_exit(1); - } - /* the daemon will respond with the same message with connect_port filled - * and empty cmdline */ - if (!read_all(s, &hdr, sizeof(hdr))) { - perror("read daemon"); - do_exit(1); - } - assert(hdr.type == type); - if (hdr.len != sizeof(params)) { - fprintf(stderr, "Invalid response for 0x%x\n", type); - do_exit(1); - } - if (!read_all(s, ¶ms, sizeof(params))) { - perror("read daemon"); - do_exit(1); - } - *data_port = params.connect_port; - *data_domain = params.connect_domain; -} - -static void send_service_connect(int s, char *conn_ident, - int connect_domain, int connect_port) -{ - struct msg_header hdr; - struct exec_params exec_params; - struct service_params srv_params; - - hdr.type = MSG_SERVICE_CONNECT; - hdr.len = sizeof(exec_params) + sizeof(srv_params); - - exec_params.connect_domain = connect_domain; - exec_params.connect_port = connect_port; - strncpy(srv_params.ident, conn_ident, sizeof(srv_params.ident) - 1); - srv_params.ident[sizeof(srv_params.ident) - 1] = '\0'; - - if (!write_all(s, &hdr, sizeof(hdr)) - || !write_all(s, &exec_params, sizeof(exec_params)) - || !write_all(s, &srv_params, sizeof(srv_params))) { - perror("write daemon"); - do_exit(1); - } -} - -static void send_exit_code(libvchan_t *vchan, int status) -{ - struct msg_header hdr; - - hdr.type = MSG_DATA_EXIT_CODE; - hdr.len = sizeof(int); - if (libvchan_send(vchan, &hdr, sizeof(hdr)) != sizeof(hdr)) { - fprintf(stderr, "Failed to write exit code to the agent\n"); - do_exit(1); - } - if (libvchan_send(vchan, &status, sizeof(status)) != sizeof(status)) { - fprintf(stderr, "Failed to write exit code(2) to the agent\n"); - do_exit(1); - } -} - -static void handle_input(libvchan_t *vchan) -{ - char buf[MAX_DATA_CHUNK]; - int ret; - size_t max_len; - struct msg_header hdr; - - max_len = libvchan_buffer_space(vchan)-sizeof(hdr); - if (max_len > sizeof(buf)) - max_len = sizeof(buf); - if (max_len == 0) - return; - ret = read(local_stdout_fd, buf, max_len); - if (ret < 0) { - perror("read"); - do_exit(1); - } - hdr.type = is_service ? MSG_DATA_STDOUT : MSG_DATA_STDIN; - hdr.len = ret; - if (libvchan_send(vchan, &hdr, sizeof(hdr)) != sizeof(hdr)) { - fprintf(stderr, "Failed to write STDIN data to the agent\n"); - do_exit(1); - } - if (ret == 0) { - close(local_stdout_fd); - local_stdout_fd = -1; - if (local_stdin_fd == -1) { - // if not a remote end of service call, wait for exit status - if (is_service) { - // if pipe in opposite direction already closed, no need to stay alive - if (local_pid == 0) { - /* if this is "remote" service end and no real local process - * exists (using own stdin/out) send also fake exit code */ - send_exit_code(vchan, 0); - do_exit(0); - } - } - } - } - if (!write_vchan_all(vchan, buf, ret)) { - if (!libvchan_is_open(vchan)) { - // agent disconnected its end of socket, so no future data will be - // send there; there is no sense to read from child stdout - // - // since vchan socket is buffered it doesn't mean all data was - // received from the agent - close(local_stdout_fd); - local_stdout_fd = -1; - if (local_stdin_fd == -1) { - // since child does no longer accept data on its stdin, doesn't - // make sense to process the data from the daemon - // - // we don't know real exit VM process code (exiting here, before - // MSG_DATA_EXIT_CODE message) - do_exit(1); - } - } else - perror("write agent"); - } -} - -void do_replace_chars(char *buf, int len) { - int i; - unsigned char c; - - for (i = 0; i < len; i++) { - c = buf[i]; - if ((c < '\040' || c > '\176') && /* not printable ASCII */ - (c != '\t') && /* not tab */ - (c != '\n') && /* not newline */ - (c != '\r') && /* not return */ - (c != '\b') && /* not backspace */ - (c != '\a')) /* not bell */ - buf[i] = '_'; - } -} - -static int handle_vchan_data(libvchan_t *vchan, struct buffer *stdin_buf) -{ - int status; - struct msg_header hdr; - char buf[MAX_DATA_CHUNK]; - - if (local_stdin_fd != -1) { - switch(flush_client_data(local_stdin_fd, stdin_buf)) { - case WRITE_STDIN_ERROR: - perror("write stdin"); - close(local_stdin_fd); - local_stdin_fd = -1; - break; - case WRITE_STDIN_BUFFERED: - return WRITE_STDIN_BUFFERED; - case WRITE_STDIN_OK: - break; - } - } - if (libvchan_recv(vchan, &hdr, sizeof hdr) < 0) { - perror("read vchan"); - do_exit(1); - } - if (hdr.len > MAX_DATA_CHUNK) { - fprintf(stderr, "client_header.len=%d\n", hdr.len); - do_exit(1); - } - if (!read_vchan_all(vchan, buf, hdr.len)) { - perror("read daemon"); - do_exit(1); - } - - switch (hdr.type) { - /* both directions because we can serve as either end of service call */ - case MSG_DATA_STDIN: - case MSG_DATA_STDOUT: - if (local_stdin_fd == -1) - break; - if (replace_chars_stdout) - do_replace_chars(buf, hdr.len); - if (hdr.len == 0) { - /* restore flags, as we may have not the only copy of this file descriptor - */ - if (local_stdin_fd != -1) - set_block(local_stdin_fd); - close(local_stdin_fd); - local_stdin_fd = -1; - } else { - switch (write_stdin(local_stdin_fd, buf, hdr.len, stdin_buf)) { - case WRITE_STDIN_BUFFERED: - return WRITE_STDIN_BUFFERED; - case WRITE_STDIN_ERROR: - if (errno == EPIPE) { - // local process have closed its stdin, handle data in oposite - // direction (if any) before exit - close(local_stdin_fd); - local_stdin_fd = -1; - } else { - perror("write local stdout"); - do_exit(1); - } - break; - case WRITE_STDIN_OK: - break; - } - } - break; - case MSG_DATA_STDERR: - if (replace_chars_stderr) - do_replace_chars(buf, hdr.len); - write_all(2, buf, hdr.len); - break; - case MSG_DATA_EXIT_CODE: - libvchan_close(vchan); - if (hdr.len < sizeof(status)) - status = 255; - else - memcpy(&status, buf, sizeof(status)); - - flush_client_data(local_stdin_fd, stdin_buf); - do_exit(status); - break; - default: - fprintf(stderr, "unknown msg %d\n", hdr.type); - do_exit(1); - } - /* intentionally do not distinguish between _ERROR and _OK, because in case - * of write error, we simply eat the data - no way to report it to the - * other side */ - return WRITE_STDIN_OK; -} - -static void check_child_status(libvchan_t *vchan) -{ - pid_t pid; - int status; - - pid = waitpid(local_pid, &status, WNOHANG); - if (pid < 0) { - perror("waitpid"); - do_exit(1); - } - if (pid == 0 || !WIFEXITED(status)) - return; - if (is_service) - send_exit_code(vchan, WEXITSTATUS(status)); - do_exit(status); -} - -static void select_loop(libvchan_t *vchan) -{ - fd_set select_set; - fd_set wr_set; - int max_fd; - int ret; - int vchan_fd; - sigset_t selectmask; - struct timespec zero_timeout = { 0, 0 }; - struct timespec select_timeout = { 10, 0 }; - struct buffer stdin_buf; - - sigemptyset(&selectmask); - sigaddset(&selectmask, SIGCHLD); - sigprocmask(SIG_BLOCK, &selectmask, NULL); - sigemptyset(&selectmask); - buffer_init(&stdin_buf); - /* remember to set back to blocking mode before closing the FD - this may - * be not the only copy and some processes may misbehave when get - * nonblocking FD for input/output - */ - set_nonblock(local_stdin_fd); - - for (;;) { - vchan_fd = libvchan_fd_for_select(vchan); - FD_ZERO(&select_set); - FD_ZERO(&wr_set); - FD_SET(vchan_fd, &select_set); - max_fd = vchan_fd; - if (local_stdout_fd != -1 && - (size_t)libvchan_buffer_space(vchan) > sizeof(struct msg_header)) { - FD_SET(local_stdout_fd, &select_set); - if (local_stdout_fd > max_fd) - max_fd = local_stdout_fd; - } - if (child_exited && local_stdout_fd == -1) - check_child_status(vchan); - if (local_stdin_fd != -1 && buffer_len(&stdin_buf)) { - FD_SET(local_stdin_fd, &wr_set); - if (local_stdin_fd > max_fd) - max_fd = local_stdin_fd; - } - if ((local_stdin_fd == -1 || buffer_len(&stdin_buf) == 0) && - libvchan_data_ready(vchan) > 0) { - /* check for other FDs, but exit immediately */ - ret = pselect(max_fd + 1, &select_set, &wr_set, NULL, - &zero_timeout, &selectmask); - } else - ret = pselect(max_fd + 1, &select_set, &wr_set, NULL, - &select_timeout, &selectmask); - if (ret < 0) { - if (errno == EINTR && local_pid > 0) { - continue; - } else { - perror("select"); - do_exit(1); - } - } - if (ret == 0) { - if (!libvchan_is_open(vchan)) { - /* remote disconnected witout a proper signaling */ - do_exit(1); - } - } - if (FD_ISSET(vchan_fd, &select_set)) - libvchan_wait(vchan); - if (buffer_len(&stdin_buf) && - local_stdin_fd != -1 && - FD_ISSET(local_stdin_fd, &wr_set)) { - if (flush_client_data(local_stdin_fd, &stdin_buf) == WRITE_STDIN_ERROR) { - perror("write stdin"); - close(local_stdin_fd); - local_stdin_fd = -1; - } - } - while (libvchan_data_ready(vchan)) - if (handle_vchan_data(vchan, &stdin_buf) != WRITE_STDIN_OK) - break; - - if (local_stdout_fd != -1 - && FD_ISSET(local_stdout_fd, &select_set)) - handle_input(vchan); - } -} - -static void usage(char *name) -{ - fprintf(stderr, - "usage: %s [-w timeout] [-W] [-t] [-T] -d domain_name [" - "-l local_prog|" - "-c request_id,src_domain_name,src_domain_id|" - "-e] remote_cmdline\n" - "-e means exit after sending cmd,\n" - "-t enables replacing problematic bytes with '_' in command output, -T is the same for stderr\n" - "-W waits for connection end even in case of VM-VM (-c)\n" - "-c: connect to existing process (response to trigger service call)\n" - "-w timeout: override default connection timeout of 5s (set 0 for no timeout)\n", - name); - exit(1); -} - -static void parse_connect(char *str, char **request_id, - char **src_domain_name, int *src_domain_id) -{ - int i=0; - char *token = NULL; - char *separators = ","; - - token = strtok(str, separators); - while (token) - { - switch (i) - { - case 0: - *request_id = token; - if (strlen(*request_id) >= sizeof(struct service_params)) { - fprintf(stderr, "Invalid -c parameter (request_id too long, max %lu)\n", - sizeof(struct service_params)-1); - exit(1); - } - break; - case 1: - *src_domain_name = token; - break; - case 2: - *src_domain_id = atoi(token); - break; - default: - fprintf(stderr, "Invalid -c parameter (should be: \"-c request_id,src_domain_name,src_domain_id\")\n"); - exit(1); - } - token = strtok(NULL, separators); - i++; - } -} - -static void sigalrm_handler(int x __attribute__((__unused__))) -{ - fprintf(stderr, "vchan connection timeout\n"); - do_exit(1); -} - -static void wait_for_vchan_client_with_timeout(libvchan_t *conn, int timeout) { - struct timeval start_tv, now_tv, timeout_tv; - - if (timeout && gettimeofday(&start_tv, NULL) == -1) { - perror("gettimeofday"); - do_exit(1); - } - while (conn && libvchan_is_open(conn) == VCHAN_WAITING) { - if (timeout) { - fd_set rdset; - int fd = libvchan_fd_for_select(conn); - - /* calculate how much time left until connection timeout expire */ - if (gettimeofday(&now_tv, NULL) == -1) { - perror("gettimeofday"); - do_exit(1); - } - timersub(&start_tv, &now_tv, &timeout_tv); - timeout_tv.tv_sec += timeout; - if (timeout_tv.tv_sec < 0) { - fprintf(stderr, "vchan connection timeout\n"); - libvchan_close(conn); - do_exit(1); - } - FD_ZERO(&rdset); - FD_SET(fd, &rdset); - switch (select(fd+1, &rdset, NULL, NULL, &timeout_tv)) { - case -1: - if (errno == EINTR) { - break; - } - fprintf(stderr, "vchan connection error\n"); - libvchan_close(conn); - do_exit(1); - break; - case 0: - fprintf(stderr, "vchan connection timeout\n"); - libvchan_close(conn); - do_exit(1); - } - } - libvchan_wait(conn); - } -} - -int main(int argc, char **argv) -{ - int opt; - char *domname = NULL; - libvchan_t *data_vchan = NULL; - int data_port; - int data_domain; - int msg_type; - int s; - int just_exec = 0; - int wait_connection_end = 0; - int connect_existing = 0; - char *local_cmdline = NULL; - char *remote_cmdline = NULL; - char *request_id; - char *src_domain_name = NULL; - int src_domain_id = 0; /* if not -c given, the process is run in dom0 */ - int connection_timeout = 5; - struct service_params svc_params; - while ((opt = getopt(argc, argv, "d:l:ec:tTw:W")) != -1) { - switch (opt) { - case 'd': - domname = strdup(optarg); - break; - case 'l': - local_cmdline = strdup(optarg); - break; - case 'e': - just_exec = 1; - break; - case 'c': - parse_connect(optarg, &request_id, &src_domain_name, &src_domain_id); - connect_existing = 1; - is_service = 1; - break; - case 't': - replace_chars_stdout = 1; - break; - case 'T': - replace_chars_stderr = 1; - break; - case 'w': - connection_timeout = atoi(optarg); - break; - case 'W': - wait_connection_end = 1; - break; - default: - usage(argv[0]); - } - } - if (optind >= argc || !domname) - usage(argv[0]); - remote_cmdline = argv[optind]; - - register_exec_func(&do_exec); - - if (just_exec + connect_existing + (local_cmdline != 0) > 1) { - fprintf(stderr, "ERROR: only one of -e, -l, -c can be specified\n"); - usage(argv[0]); - } - - if (strcmp(domname, "dom0") == 0 && !connect_existing) { - fprintf(stderr, "ERROR: when target domain is 'dom0', -c must be specified\n"); - usage(argv[0]); - } - - if (strcmp(domname, "dom0") == 0) { - if (connect_existing) { - msg_type = MSG_SERVICE_CONNECT; - strncpy(svc_params.ident, request_id, sizeof(svc_params.ident) - 1); - svc_params.ident[sizeof(svc_params.ident) - 1] = '\0'; - } else if (just_exec) - msg_type = MSG_JUST_EXEC; - else - msg_type = MSG_EXEC_CMDLINE; - assert(src_domain_name); - setenv("QREXEC_REMOTE_DOMAIN", src_domain_name, 1); - s = connect_unix_socket(src_domain_name); - negotiate_connection_params(s, - 0, /* dom0 */ - msg_type, - connect_existing ? (void*)&svc_params : (void*)remote_cmdline, - connect_existing ? sizeof(svc_params) : strlen(remote_cmdline) + 1, - &data_domain, - &data_port); - - prepare_local_fds(remote_cmdline); - if (connect_existing) { - void (*old_handler)(int); - - /* libvchan_client_init is blocking and does not support connection - * timeout, so use alarm(2) for that... */ - old_handler = signal(SIGALRM, sigalrm_handler); - alarm(connection_timeout); - data_vchan = libvchan_client_init(data_domain, data_port); - alarm(0); - signal(SIGALRM, old_handler); - } else { - data_vchan = libvchan_server_init(data_domain, data_port, - VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE); - wait_for_vchan_client_with_timeout(data_vchan, connection_timeout); - } - if (!data_vchan || !libvchan_is_open(data_vchan)) { - fprintf(stderr, "Failed to open data vchan connection\n"); - do_exit(1); - } - if (handle_agent_handshake(data_vchan, connect_existing) < 0) - do_exit(1); - select_loop(data_vchan); - } else { - if (just_exec) - msg_type = MSG_JUST_EXEC; - else - msg_type = MSG_EXEC_CMDLINE; - s = connect_unix_socket(domname); - negotiate_connection_params(s, - src_domain_id, - msg_type, - remote_cmdline, - strlen(remote_cmdline) + 1, - &data_domain, - &data_port); - if (wait_connection_end && connect_existing) - /* save socket fd, 's' will be reused for the other qrexec-daemon - * connection */ - wait_connection_end = s; - else - close(s); - setenv("QREXEC_REMOTE_DOMAIN", domname, 1); - prepare_local_fds(local_cmdline); - if (connect_existing) { - s = connect_unix_socket(src_domain_name); - send_service_connect(s, request_id, data_domain, data_port); - close(s); - if (wait_connection_end) { - /* wait for EOF */ - fd_set read_fd; - FD_ZERO(&read_fd); - FD_SET(wait_connection_end, &read_fd); - select(wait_connection_end+1, &read_fd, NULL, NULL, 0); - } - } else { - data_vchan = libvchan_server_init(data_domain, data_port, - VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE); - if (!data_vchan) { - fprintf(stderr, "Failed to start data vchan server\n"); - do_exit(1); - } - wait_for_vchan_client_with_timeout(data_vchan, connection_timeout); - if (!libvchan_is_open(data_vchan)) { - fprintf(stderr, "Failed to open data vchan connection\n"); - do_exit(1); - } - if (handle_agent_handshake(data_vchan, 0) < 0) - do_exit(1); - select_loop(data_vchan); - } - } - return 0; -} - -// vim:ts=4:sw=4:et: diff --git a/qrexec/qrexec-daemon.c b/qrexec/qrexec-daemon.c deleted file mode 100644 index 6c8faf2..0000000 --- a/qrexec/qrexec-daemon.c +++ /dev/null @@ -1,874 +0,0 @@ -/* - * The Qubes OS Project, http://www.qubes-os.org - * - * Copyright (C) 2010 Rafal Wojtczuk - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "qrexec.h" -#include "libqrexec-utils.h" - -enum client_state { - CLIENT_INVALID = 0, // table slot not used - CLIENT_HELLO, // waiting for client hello - CLIENT_CMDLINE, // waiting for cmdline from client - CLIENT_RUNNING // waiting for client termination (to release vchan port) -}; - -enum vchan_port_state { - VCHAN_PORT_UNUSED = -1 -}; - -struct _client { - int state; // enum client_state -}; - -struct _policy_pending { - pid_t pid; - struct service_params params; - int reserved_vchan_port; -}; - -#define VCHAN_BASE_DATA_PORT (VCHAN_BASE_PORT+1) - -/* - The "clients" array is indexed by client's fd. - Thus its size must be equal MAX_FDS; defining MAX_CLIENTS for clarity. - */ - -#define MAX_CLIENTS MAX_FDS -struct _client clients[MAX_CLIENTS]; // data on all qrexec_client connections - -struct _policy_pending policy_pending[MAX_CLIENTS]; -int policy_pending_max = -1; - -/* indexed with vchan port number relative to VCHAN_BASE_DATA_PORT; stores - * either VCHAN_PORT_* or remote domain id for used port */ -int used_vchan_ports[MAX_CLIENTS]; - -/* notify client (close its connection) when connection initiated by it was - * terminated - used by qrexec-policy to cleanup (disposable) VM; indexed with - * vchan port number relative to VCHAN_BASE_DATA_PORT; stores fd of given - * client or -1 if none requested */ -int vchan_port_notify_client[MAX_CLIENTS]; - -int max_client_fd = -1; // current max fd of all clients; so that we need not to scan all the "clients" table -int qrexec_daemon_unix_socket_fd; // /var/run/qubes/qrexec.xid descriptor -const char *default_user = "user"; -const char default_user_keyword[] = "DEFAULT:"; -#define default_user_keyword_len_without_colon (sizeof(default_user_keyword)-2) - -int opt_quiet = 0; - -#ifdef __GNUC__ -# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) -#else -# define UNUSED(x) UNUSED_ ## x -#endif - -volatile int children_count; - -libvchan_t *vchan; - -void sigusr1_handler(int UNUSED(x)) -{ - if (!opt_quiet) - fprintf(stderr, "connected\n"); - exit(0); -} - -void sigchld_parent_handler(int UNUSED(x)) -{ - children_count--; - /* starting value is 0 so we see dead real qrexec-daemon as -1 */ - if (children_count < 0) { - if (!opt_quiet) - fprintf(stderr, "failed\n"); - else - fprintf(stderr, "Connection to the VM failed\n"); - exit(1); - } -} - -static void sigchld_handler(int UNUSED(x)); - -char *remote_domain_name; // guess what -int remote_domain_id; - -void unlink_qrexec_socket() -{ - char socket_address[40]; - char link_to_socket_name[strlen(remote_domain_name) + sizeof(socket_address)]; - - snprintf(socket_address, sizeof(socket_address), - QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", remote_domain_id); - snprintf(link_to_socket_name, sizeof link_to_socket_name, - QREXEC_DAEMON_SOCKET_DIR "/qrexec.%s", remote_domain_name); - unlink(socket_address); - unlink(link_to_socket_name); -} - -void handle_vchan_error(const char *op) -{ - fprintf(stderr, "Error while vchan %s, exiting\n", op); - exit(1); -} - - -int create_qrexec_socket(int domid, const char *domname) -{ - char socket_address[40]; - char link_to_socket_name[strlen(domname) + sizeof(socket_address)]; - - snprintf(socket_address, sizeof(socket_address), - QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", domid); - snprintf(link_to_socket_name, sizeof link_to_socket_name, - QREXEC_DAEMON_SOCKET_DIR "/qrexec.%s", domname); - unlink(link_to_socket_name); - if (symlink(socket_address, link_to_socket_name)) { - fprintf(stderr, "symlink(%s,%s) failed: %s\n", socket_address, - link_to_socket_name, strerror (errno)); - } - atexit(unlink_qrexec_socket); - return get_server_socket(socket_address); -} - -#define MAX_STARTUP_TIME_DEFAULT 60 - -static void incompatible_protocol_error_message( - const char *domain_name, int remote_version) -{ - char text[1024]; - int ret; - struct stat buf; - ret=stat("/usr/bin/kdialog", &buf); -#define KDIALOG_CMD "kdialog --title 'Qrexec daemon' --sorry " -#define ZENITY_CMD "zenity --title 'Qrexec daemon' --warning --text " - snprintf(text, sizeof(text), - "%s" - "'Domain %s uses incompatible qrexec protocol (%d instead of %d). " - "You need to update either dom0 or VM packages.\n" - "To access this VM console do not close this error message and run:\n" - "sudo xl console -t pv %s'", - ret==0 ? KDIALOG_CMD : ZENITY_CMD, - domain_name, remote_version, QREXEC_PROTOCOL_VERSION, domain_name); -#undef KDIALOG_CMD -#undef ZENITY_CMD - system(text); -} - -int handle_agent_hello(libvchan_t *ctrl, const char *domain_name) -{ - struct msg_header hdr; - struct peer_info info; - int actual_version; - - if (libvchan_recv(ctrl, &hdr, sizeof(hdr)) != sizeof(hdr)) { - fprintf(stderr, "Failed to read agent HELLO hdr\n"); - return -1; - } - - if (hdr.type != MSG_HELLO || hdr.len != sizeof(info)) { - fprintf(stderr, "Invalid HELLO packet received: type %d, len %d\n", hdr.type, hdr.len); - return -1; - } - - if (libvchan_recv(ctrl, &info, sizeof(info)) != sizeof(info)) { - fprintf(stderr, "Failed to read agent HELLO body\n"); - return -1; - } - - actual_version = info.version < QREXEC_PROTOCOL_VERSION ? info.version : QREXEC_PROTOCOL_VERSION; - - if (actual_version != QREXEC_PROTOCOL_VERSION) { - fprintf(stderr, "Incompatible agent protocol version (remote %d, local %d)\n", info.version, QREXEC_PROTOCOL_VERSION); - incompatible_protocol_error_message(domain_name, info.version); - return -1; - } - - /* send own HELLO */ - /* those messages are the same as received from agent, but set it again for - * readability */ - hdr.type = MSG_HELLO; - hdr.len = sizeof(info); - info.version = QREXEC_PROTOCOL_VERSION; - - if (libvchan_send(ctrl, &hdr, sizeof(hdr)) != sizeof(hdr)) { - fprintf(stderr, "Failed to send HELLO hdr to agent\n"); - return -1; - } - - if (libvchan_send(ctrl, &info, sizeof(info)) != sizeof(info)) { - fprintf(stderr, "Failed to send HELLO hdr to agent\n"); - return -1; - } - - return 0; -} - -/* do the preparatory tasks, needed before entering the main event loop */ -void init(int xid) -{ - char qrexec_error_log_name[256]; - int logfd; - int i; - pid_t pid; - int startup_timeout = MAX_STARTUP_TIME_DEFAULT; - const char *startup_timeout_str = NULL; - - if (xid <= 0) { - fprintf(stderr, "domain id=0?\n"); - exit(1); - } - startup_timeout_str = getenv("QREXEC_STARTUP_TIMEOUT"); - if (startup_timeout_str) { - startup_timeout = atoi(startup_timeout_str); - if (startup_timeout <= 0) - // invalid or negative number - startup_timeout = MAX_STARTUP_TIME_DEFAULT; - } - signal(SIGUSR1, sigusr1_handler); - signal(SIGCHLD, sigchld_parent_handler); - switch (pid=fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - break; - default: - if (getenv("QREXEC_STARTUP_NOWAIT")) - exit(0); - if (!opt_quiet) - fprintf(stderr, "Waiting for VM's qrexec agent."); - for (i=0;i= MAX_CLIENTS) { - fprintf(stderr, "too many clients ?\n"); - exit(1); - } - - if (send_client_hello(fd) < 0) { - close(fd); - clients[fd].state = CLIENT_INVALID; - return; - } - - clients[fd].state = CLIENT_HELLO; - if (fd > max_client_fd) - max_client_fd = fd; -} - -static void terminate_client(int fd) -{ - int port; - clients[fd].state = CLIENT_INVALID; - close(fd); - /* if client requested vchan connection end notify, cancel it */ - for (port = 0; port < MAX_CLIENTS; port++) { - if (vchan_port_notify_client[port] == fd) - vchan_port_notify_client[port] = VCHAN_PORT_UNUSED; - } -} - -static void release_vchan_port(int port, int expected_remote_id) -{ - /* release only if was reserved for connection to given domain */ - if (used_vchan_ports[port-VCHAN_BASE_DATA_PORT] == expected_remote_id) { - used_vchan_ports[port-VCHAN_BASE_DATA_PORT] = VCHAN_PORT_UNUSED; - /* notify client if requested - it will clear notification request */ - if (vchan_port_notify_client[port-VCHAN_BASE_DATA_PORT] != VCHAN_PORT_UNUSED) - terminate_client(vchan_port_notify_client[port-VCHAN_BASE_DATA_PORT]); - } -} - -static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr) -{ - struct exec_params params; - int len = hdr->len-sizeof(params); - char buf[len]; - int use_default_user = 0; - int i; - - if (!read_all(fd, ¶ms, sizeof(params))) { - terminate_client(fd); - return 0; - } - if (!read_all(fd, buf, len)) { - terminate_client(fd); - return 0; - } - - if (hdr->type == MSG_SERVICE_CONNECT) { - /* if the service was accepted, do not send spurious - * MSG_SERVICE_REFUSED when service process itself exit with non-zero - * code */ - for (i = 0; i <= policy_pending_max; i++) { - if (policy_pending[i].pid && - strncmp(policy_pending[i].params.ident, buf, len) == 0) { - policy_pending[i].pid = 0; - while (policy_pending_max > 0 && - policy_pending[policy_pending_max].pid == 0) - policy_pending_max--; - break; - } - } - } - - if (!params.connect_port) { - struct exec_params client_params; - /* allocate port and send it to the client */ - params.connect_port = allocate_vchan_port(params.connect_domain); - if (params.connect_port <= 0) { - fprintf(stderr, "Failed to allocate new vchan port, too many clients?\n"); - terminate_client(fd); - return 0; - } - /* notify the client when this connection got terminated */ - vchan_port_notify_client[params.connect_port-VCHAN_BASE_DATA_PORT] = fd; - client_params.connect_port = params.connect_port; - client_params.connect_domain = remote_domain_id; - hdr->len = sizeof(client_params); - if (!write_all(fd, hdr, sizeof(*hdr))) { - terminate_client(fd); - release_vchan_port(params.connect_port, params.connect_domain); - return 0; - } - if (!write_all(fd, &client_params, sizeof(client_params))) { - terminate_client(fd); - release_vchan_port(params.connect_port, params.connect_domain); - return 0; - } - /* restore original len value */ - hdr->len = len+sizeof(params); - } else { - assert(params.connect_port >= VCHAN_BASE_DATA_PORT); - assert(params.connect_port < VCHAN_BASE_DATA_PORT+MAX_CLIENTS); - } - - if (!strncmp(buf, default_user_keyword, default_user_keyword_len_without_colon+1)) { - use_default_user = 1; - hdr->len -= default_user_keyword_len_without_colon; - hdr->len += strlen(default_user); - } - if (libvchan_send(vchan, hdr, sizeof(*hdr)) < 0) - handle_vchan_error("send"); - if (libvchan_send(vchan, ¶ms, sizeof(params)) < 0) - handle_vchan_error("send params"); - if (use_default_user) { - if (libvchan_send(vchan, default_user, strlen(default_user)) < 0) - handle_vchan_error("send default_user"); - if (libvchan_send(vchan, buf+default_user_keyword_len_without_colon, - len-default_user_keyword_len_without_colon) < 0) - handle_vchan_error("send buf"); - } else - if (libvchan_send(vchan, buf, len) < 0) - handle_vchan_error("send buf"); - return 1; -} - -static void handle_cmdline_message_from_client(int fd) -{ - struct msg_header hdr; - if (!read_all(fd, &hdr, sizeof hdr)) { - terminate_client(fd); - return; - } - switch (hdr.type) { - case MSG_EXEC_CMDLINE: - case MSG_JUST_EXEC: - case MSG_SERVICE_CONNECT: - break; - default: - terminate_client(fd); - return; - } - - if (!handle_cmdline_body_from_client(fd, &hdr)) - // client disconnected while sending cmdline, above call already - // cleaned up client info - return; - clients[fd].state = CLIENT_RUNNING; -} - -static void handle_client_hello(int fd) -{ - struct msg_header hdr; - struct peer_info info; - - if (!read_all(fd, &hdr, sizeof hdr)) { - terminate_client(fd); - return; - } - if (hdr.type != MSG_HELLO || hdr.len != sizeof(info)) { - fprintf(stderr, "Invalid HELLO packet received from client %d: " - "type %d, len %d\n", fd, hdr.type, hdr.len); - terminate_client(fd); - return; - } - if (!read_all(fd, &info, sizeof info)) { - terminate_client(fd); - return; - } - if (info.version != QREXEC_PROTOCOL_VERSION) { - fprintf(stderr, "Incompatible client protocol version (remote %d, local %d)\n", info.version, QREXEC_PROTOCOL_VERSION); - terminate_client(fd); - return; - } - clients[fd].state = CLIENT_CMDLINE; -} - -/* handle data received from one of qrexec_client processes */ -static void handle_message_from_client(int fd) -{ - char buf[MAX_DATA_CHUNK]; - - switch (clients[fd].state) { - case CLIENT_HELLO: - handle_client_hello(fd); - return; - case CLIENT_CMDLINE: - handle_cmdline_message_from_client(fd); - return; - case CLIENT_RUNNING: - // expected EOF - if (read(fd, buf, sizeof(buf)) != 0) { - fprintf(stderr, "Unexpected data received from client %d\n", fd); - } - terminate_client(fd); - return; - default: - fprintf(stderr, "Invalid client state %d\n", clients[fd].state); - exit(1); - } -} - - -/* - * The signal handler executes asynchronously; therefore all it should do is - * to set a flag "signal has arrived", and let the main even loop react to this - * flag in appropriate moment. - */ - -int child_exited; - -static void sigchld_handler(int UNUSED(x)) -{ - child_exited = 1; - signal(SIGCHLD, sigchld_handler); -} - -static void send_service_refused(libvchan_t *vchan, struct service_params *params) { - struct msg_header hdr; - - hdr.type = MSG_SERVICE_REFUSED; - hdr.len = sizeof(*params); - - if (libvchan_send(vchan, &hdr, sizeof(hdr)) != sizeof(hdr)) { - fprintf(stderr, "Failed to send MSG_SERVICE_REFUSED hdr to agent\n"); - exit(1); - } - - if (libvchan_send(vchan, params, sizeof(*params)) != sizeof(*params)) { - fprintf(stderr, "Failed to send MSG_SERVICE_REFUSED to agent\n"); - exit(1); - } -} - -/* clean zombies, check for denied service calls */ -static void reap_children() -{ - int status; - int i; - - pid_t pid; - while ((pid=waitpid(-1, &status, WNOHANG)) > 0) { - for (i = 0; i <= policy_pending_max; i++) { - if (policy_pending[i].pid == pid) { - status = WEXITSTATUS(status); - if (status != 0) { - send_service_refused(vchan, &policy_pending[i].params); - } - /* in case of allowed calls, we will do the rest in - * MSG_SERVICE_CONNECT from client handler */ - policy_pending[i].pid = 0; - while (policy_pending_max > 0 && - policy_pending[policy_pending_max].pid == 0) - policy_pending_max--; - break; - } - } - } - child_exited = 0; -} - -static int find_policy_pending_slot() { - int i; - - for (i = 0; i < MAX_CLIENTS; i++) { - if (policy_pending[i].pid == 0) { - if (i > policy_pending_max) - policy_pending_max = i; - return i; - } - } - return -1; -} - -static void sanitize_name(char * untrusted_s_signed, char *extra_allowed_chars) -{ - unsigned char * untrusted_s; - for (untrusted_s=(unsigned char*)untrusted_s_signed; *untrusted_s; untrusted_s++) { - if (*untrusted_s >= 'a' && *untrusted_s <= 'z') - continue; - if (*untrusted_s >= 'A' && *untrusted_s <= 'Z') - continue; - if (*untrusted_s >= '0' && *untrusted_s <= '9') - continue; - if (*untrusted_s == '_' || - *untrusted_s == '-' || - *untrusted_s == '.') - continue; - if (extra_allowed_chars && strchr(extra_allowed_chars, *untrusted_s)) - continue; - *untrusted_s = '_'; - } -} - -#define ENSURE_NULL_TERMINATED(x) x[sizeof(x)-1] = 0 - -/* - * Called when agent sends a message asking to execute a predefined command. - */ - -static void handle_execute_service(void) -{ - int i; - int policy_pending_slot; - pid_t pid; - struct trigger_service_params untrusted_params, params; - char remote_domain_id_str[10]; - - if (libvchan_recv(vchan, &untrusted_params, sizeof(untrusted_params)) < 0) - handle_vchan_error("recv params"); - - /* sanitize start */ - ENSURE_NULL_TERMINATED(untrusted_params.service_name); - ENSURE_NULL_TERMINATED(untrusted_params.target_domain); - ENSURE_NULL_TERMINATED(untrusted_params.request_id.ident); - sanitize_name(untrusted_params.service_name, "+"); - sanitize_name(untrusted_params.target_domain, "@:"); - sanitize_name(untrusted_params.request_id.ident, " "); - params = untrusted_params; - /* sanitize end */ - - policy_pending_slot = find_policy_pending_slot(); - if (policy_pending_slot < 0) { - fprintf(stderr, "Service request denied, too many pending requests\n"); - send_service_refused(vchan, &untrusted_params.request_id); - return; - } - - switch (pid=fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - break; - default: - policy_pending[policy_pending_slot].pid = pid; - policy_pending[policy_pending_slot].params = untrusted_params.request_id; - return; - } - for (i = 3; i < MAX_FDS; i++) - close(i); - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); - snprintf(remote_domain_id_str, sizeof(remote_domain_id_str), "%d", - remote_domain_id); - execl("/usr/bin/qrexec-policy", "qrexec-policy", "--", - remote_domain_id_str, remote_domain_name, params.target_domain, - params.service_name, params.request_id.ident, NULL); - perror("execl"); - _exit(1); -} - -static void handle_connection_terminated() -{ - struct exec_params untrusted_params, params; - - if (libvchan_recv(vchan, &untrusted_params, sizeof(untrusted_params)) < 0) - handle_vchan_error("recv params"); - /* sanitize start */ - if (untrusted_params.connect_port < VCHAN_BASE_DATA_PORT || - untrusted_params.connect_port >= VCHAN_BASE_DATA_PORT+MAX_CLIENTS) { - fprintf(stderr, "Invalid port in MSG_CONNECTION_TERMINATED (%d)\n", - untrusted_params.connect_port); - exit(1); - } - /* untrusted_params.connect_domain even if invalid will not harm - in worst - * case the port will not be released */ - params = untrusted_params; - /* sanitize end */ - release_vchan_port(params.connect_port, params.connect_domain); -} - -static void sanitize_message_from_agent(struct msg_header *untrusted_header) -{ - switch (untrusted_header->type) { - case MSG_TRIGGER_SERVICE: - if (untrusted_header->len != sizeof(struct trigger_service_params)) { - fprintf(stderr, "agent sent invalid MSG_TRIGGER_SERVICE packet\n"); - exit(1); - } - break; - case MSG_CONNECTION_TERMINATED: - if (untrusted_header->len != sizeof(struct exec_params)) { - fprintf(stderr, "agent sent invalid MSG_CONNECTION_TERMINATED packet\n"); - exit(1); - } - break; - default: - fprintf(stderr, "unknown mesage type 0x%x from agent\n", - untrusted_header->type); - exit(1); - } -} - -static void handle_message_from_agent(void) -{ - struct msg_header hdr, untrusted_hdr; - - if (libvchan_recv(vchan, &untrusted_hdr, sizeof(untrusted_hdr)) < 0) - handle_vchan_error("recv hdr"); - /* sanitize start */ - sanitize_message_from_agent(&untrusted_hdr); - hdr = untrusted_hdr; - /* sanitize end */ - - // fprintf(stderr, "got %x %x %x\n", hdr.type, hdr.client_id, - // hdr.len); - - switch (hdr.type) { - case MSG_TRIGGER_SERVICE: - handle_execute_service(); - return; - case MSG_CONNECTION_TERMINATED: - handle_connection_terminated(); - return; - } -} - -/* - * Scan the "clients" table, add ones we want to read from (because the other - * end has not send MSG_XOFF on them) to read_fdset, add ones we want to write - * to (because its pipe is full) to write_fdset. Return the highest used file - * descriptor number, needed for the first select() parameter. - */ -static int fill_fdsets_for_select(fd_set * read_fdset, fd_set * write_fdset) -{ - int i; - int max = -1; - FD_ZERO(read_fdset); - FD_ZERO(write_fdset); - for (i = 0; i <= max_client_fd; i++) { - if (clients[i].state != CLIENT_INVALID) { - FD_SET(i, read_fdset); - max = i; - } - } - FD_SET(qrexec_daemon_unix_socket_fd, read_fdset); - if (qrexec_daemon_unix_socket_fd > max) - max = qrexec_daemon_unix_socket_fd; - return max; -} - -int main(int argc, char **argv) -{ - fd_set read_fdset, write_fdset; - int i, opt; - int max; - sigset_t chld_set; - - while ((opt=getopt(argc, argv, "q")) != -1) { - switch (opt) { - case 'q': - opt_quiet = 1; - break; - default: /* '?' */ - fprintf(stderr, "usage: %s [-q] domainid domain-name [default user]\n", argv[0]); - exit(1); - } - } - if (argc - optind < 2 || argc - optind > 3) { - fprintf(stderr, "usage: %s [-q] domainid domain-name [default user]\n", argv[0]); - exit(1); - } - remote_domain_id = atoi(argv[optind]); - remote_domain_name = argv[optind+1]; - if (argc - optind >= 3) - default_user = argv[optind+2]; - init(remote_domain_id); - sigemptyset(&chld_set); - sigaddset(&chld_set, SIGCHLD); - signal(SIGCHLD, sigchld_handler); - /* - * The main event loop. Waits for one of the following events: - * - message from client - * - message from agent - * - new client - * - child exited - */ - for (;;) { - max = fill_fdsets_for_select(&read_fdset, &write_fdset); - if (libvchan_buffer_space(vchan) <= (int)sizeof(struct msg_header)) - FD_ZERO(&read_fdset); // vchan full - don't read from clients - - sigprocmask(SIG_BLOCK, &chld_set, NULL); - if (child_exited) - reap_children(); - wait_for_vchan_or_argfd(vchan, max, &read_fdset, &write_fdset); - sigprocmask(SIG_UNBLOCK, &chld_set, NULL); - - if (FD_ISSET(qrexec_daemon_unix_socket_fd, &read_fdset)) - handle_new_client(); - - while (libvchan_data_ready(vchan)) - handle_message_from_agent(); - - for (i = 0; i <= max_client_fd; i++) - if (clients[i].state != CLIENT_INVALID - && FD_ISSET(i, &read_fdset)) - handle_message_from_client(i); - } -} - -// vim:ts=4:sw=4:et: diff --git a/qrexec/qubes-rpc-multiplexer b/qrexec/qubes-rpc-multiplexer deleted file mode 100755 index 1aedb4f..0000000 --- a/qrexec/qubes-rpc-multiplexer +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh - -mkfifo /tmp/qrexec-rpc-stderr.$$ -logger -t "$1-$2" -f /tmp/qrexec-rpc-stderr.$$ >/dev/null 2>&1 /tmp/qrexec-rpc-stderr.$$ -rm -f /tmp/qrexec-rpc-stderr.$$ - -QUBES_RPC=/etc/qubes-rpc -LOCAL_QUBES_RPC=/usr/local/etc/qubes-rpc - -if ! [ $# = 2 -o $# = 4 ] ; then - echo "$0: bad argument count, usage: $0 SERVICE-NAME REMOTE-DOMAIN-NAME [REQUESTED_TARGET_TYPE REQUESTED_TARGET]" >&2 - exit 1 -fi -export QREXEC_REQUESTED_TARGET_TYPE="$3" -if [ "$QREXEC_REQUESTED_TARGET_TYPE" = "name" ]; then - export QREXEC_REQUESTED_TARGET="$4" -elif [ "$QREXEC_REQUESTED_TARGET_TYPE" = "keyword" ]; then - export QREXEC_REQUESTED_TARGET_KEYWORD="$4" -fi -# else: requested target type unknown or not given, ignore -export QREXEC_REMOTE_DOMAIN="$2" -export QREXEC_SERVICE_FULL_NAME="$1" -SERVICE_WITHOUT_ARGUMENT="${1%%+*}" -if [ "${QREXEC_SERVICE_FULL_NAME}" != "${SERVICE_WITHOUT_ARGUMENT}" ]; then - export QREXEC_SERVICE_ARGUMENT="${QREXEC_SERVICE_FULL_NAME#*+}" -fi - -for CFG_FILE in $LOCAL_QUBES_RPC/"$1" $QUBES_RPC/"$1" \ - $LOCAL_QUBES_RPC/"${SERVICE_WITHOUT_ARGUMENT}" \ - $QUBES_RPC/"${SERVICE_WITHOUT_ARGUMENT}"; do - if [ -s "$CFG_FILE" ]; then - break - fi -done - -if [ -x "$CFG_FILE" ] ; then - exec "$CFG_FILE" ${QREXEC_SERVICE_ARGUMENT} - echo "$0: failed to execute handler for" "$1" >&2 - exit 1 -else - exec /bin/sh -- "$CFG_FILE" ${QREXEC_SERVICE_ARGUMENT} - echo "$0: failed to execute handler for" "$1" >&2 - exit 1 -fi diff --git a/rpm_spec/core-dom0-linux.spec.in b/rpm_spec/core-dom0-linux.spec.in index 097d37f..71b2b10 100644 --- a/rpm_spec/core-dom0-linux.spec.in +++ b/rpm_spec/core-dom0-linux.spec.in @@ -39,10 +39,10 @@ URL: http://www.qubes-os.org BuildRequires: ImageMagick BuildRequires: pandoc BuildRequires: qubes-utils-devel >= 3.1.3 -BuildRequires: qubes-libvchan-devel BuildRequires: gcc Requires: qubes-core-dom0 Requires: python3-qubesadmin +Requires: qubes-core-qrexec-dom0 Requires: qubes-core-admin-client Requires: qubes-utils >= 3.1.3 Requires: qubes-utils-libs >= 4.0.16 @@ -75,7 +75,6 @@ Kernel install hook for Xen-based system. %build export BACKEND_VMM=@BACKEND_VMM@ (cd dom0-updates; make) -(cd qrexec; make) (cd file-copy-vm; make) (cd doc; make manpages) @@ -97,15 +96,6 @@ install -m 0664 -D dom0-updates/qubes.ReceiveUpdates.policy $RPM_BUILD_ROOT/etc/ install -d $RPM_BUILD_ROOT/var/lib/qubes/updates -# Qrexec -mkdir -p $RPM_BUILD_ROOT/usr/bin $RPM_BUILD_ROOT/usr/sbin -install qrexec/qrexec-daemon $RPM_BUILD_ROOT/usr/sbin/ -install qrexec/qrexec-client $RPM_BUILD_ROOT/usr/bin/ -# XXX: Backward compatibility -ln -s ../../bin/qrexec-client $RPM_BUILD_ROOT/usr/lib/qubes/qrexec-client -ln -s ../../sbin/qrexec-daemon $RPM_BUILD_ROOT/usr/lib/qubes/qrexec-daemon -cp qrexec/qubes-rpc-multiplexer $RPM_BUILD_ROOT/usr/lib/qubes - ### pm-utils mkdir -p $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d cp pm-utils/52qubes-pause-vms $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/ @@ -213,13 +203,6 @@ chmod -x /etc/grub.d/10_linux %{_dracutmoddir}/90qubes-pciback/* %dir %{_dracutmoddir}/90extra-modules %{_dracutmoddir}/90extra-modules/* -# Qrexec -/usr/sbin/qrexec-daemon -/usr/bin/qrexec-client -/usr/lib/qubes/qubes-rpc-multiplexer -# compat symlinks -/usr/lib/qubes/qrexec-client -/usr/lib/qubes/qrexec-daemon # file copy /usr/bin/qvm-copy-to-vm /usr/bin/qvm-move-to-vm