From 3c3252b2a34013cdcd800e3587f4c9f14a346c64 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 6 Mar 2013 18:42:00 +0100 Subject: [PATCH] Remove qrexec - moved to separate package --- qrexec/.gitignore | 4 - qrexec/Makefile | 16 - qrexec/README.rpc | 64 ---- qrexec/buffer.c | 99 ----- qrexec/buffer.h | 32 -- qrexec/exec.c | 74 ---- qrexec/glue.h | 48 --- qrexec/qrexec.h | 106 ------ qrexec/qrexec_agent.c | 595 ------------------------------ qrexec/qrexec_client.c | 295 --------------- qrexec/qrexec_client_vm.c | 109 ------ qrexec/qrexec_daemon.c | 687 ----------------------------------- qrexec/qrexec_policy | 182 ---------- qrexec/qubes_rpc_multiplexer | 15 - qrexec/txrx-vchan.c | 221 ----------- qrexec/unix_server.c | 68 ---- qrexec/write_stdin.c | 137 ------- rpm_spec/core-dom0.spec | 13 +- 18 files changed, 1 insertion(+), 2764 deletions(-) delete mode 100644 qrexec/.gitignore delete mode 100644 qrexec/Makefile delete mode 100644 qrexec/README.rpc delete mode 100644 qrexec/buffer.c delete mode 100644 qrexec/buffer.h delete mode 100644 qrexec/exec.c delete mode 100644 qrexec/glue.h delete mode 100644 qrexec/qrexec.h delete mode 100644 qrexec/qrexec_agent.c delete mode 100644 qrexec/qrexec_client.c delete mode 100644 qrexec/qrexec_client_vm.c delete mode 100644 qrexec/qrexec_daemon.c delete mode 100755 qrexec/qrexec_policy delete mode 100755 qrexec/qubes_rpc_multiplexer delete mode 100644 qrexec/txrx-vchan.c delete mode 100644 qrexec/unix_server.c delete mode 100644 qrexec/write_stdin.c diff --git a/qrexec/.gitignore b/qrexec/.gitignore deleted file mode 100644 index cc4700d0..00000000 --- a/qrexec/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -qrexec_agent -qrexec_client -qrexec_daemon -qrexec_client_vm diff --git a/qrexec/Makefile b/qrexec/Makefile deleted file mode 100644 index e66c5783..00000000 --- a/qrexec/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -CC=gcc -CFLAGS+=-g -Wall -I../vchan -I../qubes_rpc -pie -fPIC -XENLIBS=-lvchan -lu2mfn -lxenstore -lxenctrl -COMMONIOALL=../qubes_rpc/ioall.o - -all: qrexec_daemon qrexec_agent qrexec_client qrexec_client_vm -qrexec_daemon: qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o buffer.o write_stdin.o - $(CC) -pie -L../vchan -L../u2mfn -g -o qrexec_daemon qrexec_daemon.o unix_server.o $(COMMONIOALL) txrx-vchan.o write_stdin.o buffer.o $(XENLIBS) -qrexec_agent: qrexec_agent.o unix_server.o exec.o txrx-vchan.o write_stdin.o buffer.o $(COMMONIOALL) - $(CC) -pie -L../vchan -L../u2mfn -g -o qrexec_agent qrexec_agent.o unix_server.o exec.o txrx-vchan.o write_stdin.o buffer.o $(COMMONIOALL) $(XENLIBS) -qrexec_client: qrexec_client.o $(COMMONIOALL) exec.o - $(CC) -pie -g -o qrexec_client qrexec_client.o $(COMMONIOALL) exec.o -qrexec_client_vm: qrexec_client_vm.o - $(CC) -pie -g -o qrexec_client_vm qrexec_client_vm.o -clean: - rm -f *.o *~ qrexec_daemon qrexec_agent qrexec_client qrexec_client_vm diff --git a/qrexec/README.rpc b/qrexec/README.rpc deleted file mode 100644 index 63af3005..00000000 --- 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/buffer.c b/qrexec/buffer.c deleted file mode 100644 index 55f195b3..00000000 --- a/qrexec/buffer.c +++ /dev/null @@ -1,99 +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 "buffer.h" - -#define BUFFER_LIMIT 50000000 -static int total_mem; -static char *limited_malloc(int len) -{ - char *ret; - total_mem += len; - if (total_mem > BUFFER_LIMIT) { - fprintf(stderr, "attempt to allocate >BUFFER_LIMIT\n"); - exit(1); - } - ret = malloc(len); - if (!ret) { - perror("malloc"); - exit(1); - } - return ret; -} - -static void limited_free(char *ptr, int len) -{ - free(ptr); - total_mem -= len; -} - -void buffer_init(struct buffer *b) -{ - b->buflen = 0; - b->data = NULL; -} - -void buffer_free(struct buffer *b) -{ - if (b->buflen) - limited_free(b->data, b->buflen); - buffer_init(b); -} - -/* -The following two functions can be made much more efficient. -Yet the profiling output show they are not significant CPU hogs, so -we keep them so simple to make them obviously correct. -*/ - -void buffer_append(struct buffer *b, char *data, int len) -{ - int newsize = len + b->buflen; - char *qdata = limited_malloc(len + b->buflen); - memcpy(qdata, b->data, b->buflen); - memcpy(qdata + b->buflen, data, len); - buffer_free(b); - b->buflen = newsize; - b->data = qdata; -} - -void buffer_remove(struct buffer *b, int len) -{ - int newsize = b->buflen - len; - char *qdata = limited_malloc(newsize); - memcpy(qdata, b->data + len, newsize); - buffer_free(b); - b->buflen = newsize; - b->data = qdata; -} - -int buffer_len(struct buffer *b) -{ - return b->buflen; -} - -void *buffer_data(struct buffer *b) -{ - return b->data; -} diff --git a/qrexec/buffer.h b/qrexec/buffer.h deleted file mode 100644 index 80b3779d..00000000 --- a/qrexec/buffer.h +++ /dev/null @@ -1,32 +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. - * - */ - -struct buffer { - char *data; - int buflen; -}; - -void buffer_init(struct buffer *b); -void buffer_free(struct buffer *b); -void buffer_append(struct buffer *b, char *data, int len); -void buffer_remove(struct buffer *b, int len); -int buffer_len(struct buffer *b); -void *buffer_data(struct buffer *b); diff --git a/qrexec/exec.c b/qrexec/exec.c deleted file mode 100644 index a87f5050..00000000 --- a/qrexec/exec.c +++ /dev/null @@ -1,74 +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 - -extern void do_exec(char *); - -void fix_fds(int fdin, int fdout, int fderr) -{ - int i; - for (i = 0; i < 256; i++) - if (i != fdin && i != fdout && i != fderr) - close(i); - dup2(fdin, 0); - dup2(fdout, 1); - dup2(fderr, 2); - close(fdin); - close(fdout); - if (fderr != 2) - close(fderr); -} - -void do_fork_exec(char *cmdline, int *pid, int *stdin_fd, int *stdout_fd, - int *stderr_fd) -{ - int inpipe[2], outpipe[2], errpipe[2]; - - if (pipe(inpipe) || pipe(outpipe) || (stderr_fd && pipe(errpipe))) { - perror("pipe"); - exit(1); - } - switch (*pid = fork()) { - case -1: - perror("fork"); - exit(-1); - case 0: - if (stderr_fd) { - fix_fds(inpipe[0], outpipe[1], errpipe[1]); - } else - fix_fds(inpipe[0], outpipe[1], 2); - - do_exec(cmdline); - exit(-1); - default:; - } - close(inpipe[0]); - close(outpipe[1]); - *stdin_fd = inpipe[1]; - *stdout_fd = outpipe[0]; - if (stderr_fd) { - close(errpipe[1]); - *stderr_fd = errpipe[0]; - } -} diff --git a/qrexec/glue.h b/qrexec/glue.h deleted file mode 100644 index 0d92474c..00000000 --- a/qrexec/glue.h +++ /dev/null @@ -1,48 +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 - -void do_fork_exec(char *cmdline, int *pid, int *stdin_fd, int *stdout_fd, - int *stderr_fd); -int peer_server_init(int port); -char *peer_client_init(int dom, int port); -void wait_for_vchan_or_argfd(int max, fd_set * rdset, fd_set * wrset); -int read_ready_vchan_ext(); -int read_all_vchan_ext(void *buf, int size); -int write_all_vchan_ext(void *buf, int size); -int buffer_space_vchan_ext(); -void fix_fds(int fdin, int fdout, int fderr); - -int get_server_socket(char *); -int do_accept(int s); - -enum { - WRITE_STDIN_OK = 0x200, - WRITE_STDIN_BUFFERED, - WRITE_STDIN_ERROR -}; - -int flush_client_data(int fd, int client_id, struct buffer *buffer); -int write_stdin(int fd, int client_id, char *data, int len, - struct buffer *buffer); -void set_nonblock(int fd); -int fork_and_flush_stdin(int fd, struct buffer *buffer); diff --git a/qrexec/qrexec.h b/qrexec/qrexec.h deleted file mode 100644 index d19126dd..00000000 --- a/qrexec/qrexec.h +++ /dev/null @@ -1,106 +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. - * - */ - -/* See also http://wiki.qubes-os.org/trac/wiki/Qrexec */ - -#define QREXEC_DAEMON_SOCKET_DIR "/var/run/qubes" -#define MAX_FDS 256 -#define MAX_DATA_CHUNK 4096 - -#define REXEC_PORT 512 - -#define QREXEC_AGENT_TRIGGER_PATH "/var/run/qubes/qrexec_agent" -#define QREXEC_AGENT_FDPASS_PATH "/var/run/qubes/qrexec_agent_fdpass" -#define MEMINFO_WRITER_PIDFILE "/var/run/meminfo-writer.pid" -#define QUBES_RPC_MULTIPLEXER_PATH "/usr/lib/qubes/qubes_rpc_multiplexer" - -#define QUBES_RPC_MAGIC_CMD "QUBESRPC" - -enum { - /* messages from qrexec_client to qrexec_daemon (both in dom0) */ - /* start process in VM and pass its stdin/out/err to dom0 */ - MSG_CLIENT_TO_SERVER_EXEC_CMDLINE = 0x100, - /* start process in VM discarding its stdin/out/err (connect to /dev/null) */ - MSG_CLIENT_TO_SERVER_JUST_EXEC, - /* connect to existing process in VM to receive its stdin/out/err - * struct connect_existing_params passed as data */ - MSG_CLIENT_TO_SERVER_CONNECT_EXISTING, - - /* messages qrexec_daemon(dom0)->qrexec_agent(VM) */ - /* same as MSG_CLIENT_TO_SERVER_CONNECT_EXISTING */ - MSG_SERVER_TO_AGENT_CONNECT_EXISTING, - /* same as MSG_CLIENT_TO_SERVER_EXEC_CMDLINE */ - MSG_SERVER_TO_AGENT_EXEC_CMDLINE, - /* same as MSG_CLIENT_TO_SERVER_JUST_EXEC */ - MSG_SERVER_TO_AGENT_JUST_EXEC, - /* pass data to process stdin */ - MSG_SERVER_TO_AGENT_INPUT, - /* detach from process; qrexec_agent should close pipes to process - * stdin/out/err; it's up to the VM child process if it cause its termination */ - MSG_SERVER_TO_AGENT_CLIENT_END, - - /* flow control, qrexec_daemon->qrexec_agent */ - /* suspend reading of named fd from child process */ - MSG_XOFF, - /* resume reading of named fd from child process */ - MSG_XON, - - /* messages qrexec_agent(VM)->qrexec_daemon(dom0) */ - /* pass data from process stdout */ - MSG_AGENT_TO_SERVER_STDOUT, - /* pass data from process stderr */ - MSG_AGENT_TO_SERVER_STDERR, - /* inform that process terminated and pass its exit code; this should be - * send after all data from stdout/err are send */ - MSG_AGENT_TO_SERVER_EXIT_CODE, - /* call Qubes RPC service - * struct trigger_connect_params passed as data */ - MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING, - - /* messages qrexec_daemon->qrexec_client (both in dom0) */ - /* same as MSG_AGENT_TO_SERVER_STDOUT */ - MSG_SERVER_TO_CLIENT_STDOUT, - /* same as MSG_AGENT_TO_SERVER_STDERR */ - MSG_SERVER_TO_CLIENT_STDERR, - /* same as MSG_AGENT_TO_SERVER_EXIT_CODE */ - MSG_SERVER_TO_CLIENT_EXIT_CODE -}; - -struct server_header { - unsigned int type; - unsigned int client_id; - unsigned int len; -}; - -struct client_header { - unsigned int type; - unsigned int len; -}; - -struct connect_existing_params { - char ident[32]; -}; - -struct trigger_connect_params { - char exec_index[64]; - char target_vmname[32]; - struct connect_existing_params process_fds; -}; diff --git a/qrexec/qrexec_agent.c b/qrexec/qrexec_agent.c deleted file mode 100644 index f5ac69b0..00000000 --- a/qrexec/qrexec_agent.c +++ /dev/null @@ -1,595 +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 "buffer.h" -#include "glue.h" - -enum fdtype { - FDTYPE_INVALID, - FDTYPE_STDOUT, - FDTYPE_STDERR -}; - -struct _process_fd { - int client_id; - int type; - int is_blocked; -}; -struct _client_info { - int stdin_fd; - int stdout_fd; - int stderr_fd; - - int exit_status; - int is_exited; - int pid; - int is_blocked; - int is_close_after_flush_needed; - struct buffer buffer; -}; - -int max_process_fd = -1; - -/* indexed by file descriptor */ -struct _process_fd process_fd[MAX_FDS]; - -/* indexed by client id, which is descriptor number of a client in daemon */ -struct _client_info client_info[MAX_FDS]; - -int trigger_fd; -int passfd_socket; - -int meminfo_write_started = 0; - -void init() -{ - peer_server_init(REXEC_PORT); - umask(0); - mkfifo(QREXEC_AGENT_TRIGGER_PATH, 0666); - passfd_socket = get_server_socket(QREXEC_AGENT_FDPASS_PATH); - umask(077); - trigger_fd = - open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK); -} - -void wake_meminfo_writer() { - FILE *f; - pid_t pid; - - if (meminfo_write_started) - /* wake meminfo-writer only once */ - return; - - f = fopen(MEMINFO_WRITER_PIDFILE, "r"); - if (f == NULL) { - /* no meminfo-writer found, ignoring */ - return; - } - if (fscanf(f, "%d", &pid) < 1) { - /* no meminfo-writer found, ignoring */ - return; - } - - fclose(f); - kill(pid, SIGUSR1); - meminfo_write_started = 1; -} - -void no_colon_in_cmd() -{ - fprintf(stderr, - "cmdline is supposed to be in user:command form\n"); - exit(1); -} - -void do_exec(char *cmd) -{ - char buf[strlen(QUBES_RPC_MULTIPLEXER_PATH) + strlen(cmd) - strlen(QUBES_RPC_MAGIC_CMD) + 1]; - char *realcmd = index(cmd, ':'); - if (!realcmd) - no_colon_in_cmd(); - /* mark end of username and move to command */ - *realcmd = 0; - realcmd++; - /* ignore "nogui:" prefix in linux agent */ - if (strncmp(realcmd, "nogui:", 6) == 0) - realcmd+=6; - /* replace magic RPC cmd with RPC multiplexer path */ - if (strncmp(realcmd, QUBES_RPC_MAGIC_CMD " ", strlen(QUBES_RPC_MAGIC_CMD)+1)==0) { - strcpy(buf, QUBES_RPC_MULTIPLEXER_PATH); - strcpy(buf + strlen(QUBES_RPC_MULTIPLEXER_PATH), realcmd + strlen(QUBES_RPC_MAGIC_CMD)); - realcmd = buf; - } - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); - - execl("/bin/su", "su", "-", cmd, "-c", realcmd, NULL); - perror("execl"); - exit(1); -} - -void handle_just_exec(int client_id, int len) -{ - char buf[len]; - int fdn, pid; - - read_all_vchan_ext(buf, len); - switch (pid = fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - fdn = open("/dev/null", O_RDWR); - fix_fds(fdn, fdn, fdn); - do_exec(buf); - perror("execl"); - exit(1); - default:; - } - fprintf(stderr, "executed (nowait) %s pid %d\n", buf, pid); -} - -void create_info_about_client(int client_id, int pid, int stdin_fd, - int stdout_fd, int stderr_fd) -{ - process_fd[stdout_fd].client_id = client_id; - process_fd[stdout_fd].type = FDTYPE_STDOUT; - process_fd[stdout_fd].is_blocked = 0; - process_fd[stderr_fd].client_id = client_id; - process_fd[stderr_fd].type = FDTYPE_STDERR; - process_fd[stderr_fd].is_blocked = 0; - - if (stderr_fd > max_process_fd) - max_process_fd = stderr_fd; - if (stdout_fd > max_process_fd) - max_process_fd = stdout_fd; - - set_nonblock(stdin_fd); - - client_info[client_id].stdin_fd = stdin_fd; - client_info[client_id].stdout_fd = stdout_fd; - client_info[client_id].stderr_fd = stderr_fd; - client_info[client_id].exit_status = 0; - client_info[client_id].is_exited = 0; - client_info[client_id].pid = pid; - client_info[client_id].is_blocked = 0; - client_info[client_id].is_close_after_flush_needed = 0; - buffer_init(&client_info[client_id].buffer); -} - -void handle_exec(int client_id, int len) -{ - char buf[len]; - int pid, stdin_fd, stdout_fd, stderr_fd; - - read_all_vchan_ext(buf, len); - - do_fork_exec(buf, &pid, &stdin_fd, &stdout_fd, &stderr_fd); - - create_info_about_client(client_id, pid, stdin_fd, stdout_fd, - stderr_fd); - - fprintf(stderr, "executed %s pid %d\n", buf, pid); - -} - -void handle_connect_existing(int client_id, int len) -{ - int stdin_fd, stdout_fd, stderr_fd; - char buf[len]; - read_all_vchan_ext(buf, len); - sscanf(buf, "%d %d %d", &stdin_fd, &stdout_fd, &stderr_fd); - create_info_about_client(client_id, -1, stdin_fd, stdout_fd, - stderr_fd); - client_info[client_id].is_exited = 1; //do not wait for SIGCHLD -} - -void update_max_process_fd() -{ - int i; - for (i = max_process_fd; - process_fd[i].type == FDTYPE_INVALID && i >= 0; i--); - max_process_fd = i; -} - -void send_exit_code(int client_id, int status) -{ - struct server_header s_hdr; - s_hdr.type = MSG_AGENT_TO_SERVER_EXIT_CODE; - s_hdr.client_id = client_id; - s_hdr.len = sizeof status; - write_all_vchan_ext(&s_hdr, sizeof s_hdr); - write_all_vchan_ext(&status, sizeof(status)); - fprintf(stderr, "send exit code %d for client_id %d pid %d\n", - status, client_id, client_info[client_id].pid); -} - - -// erase process data structures, possibly forced by remote -void remove_process(int client_id, int status) -{ - int i; - if (!client_info[client_id].pid) - return; - if (client_info[client_id].stdin_fd >= 0) - fork_and_flush_stdin(client_info[client_id].stdin_fd, - &client_info[client_id].buffer); -#if 0 -// let's let it die by itself, possibly after it has received buffered stdin - kill(client_info[client_id].pid, SIGKILL); -#endif - if (status != -1) - send_exit_code(client_id, status); - - - close(client_info[client_id].stdin_fd); - client_info[client_id].pid = 0; - client_info[client_id].stdin_fd = -1; - client_info[client_id].is_blocked = 0; - buffer_free(&client_info[client_id].buffer); - - for (i = 0; i <= max_process_fd; i++) - if (process_fd[i].type != FDTYPE_INVALID - && process_fd[i].client_id == client_id) { - process_fd[i].type = FDTYPE_INVALID; - process_fd[i].client_id = -1; - process_fd[i].is_blocked = 0; - close(i); - } - update_max_process_fd(); -} - -// remove process not immediately after it has exited, but after its stdout and stderr has been drained -// previous method implemented in flush_out_err was broken - it cannot work when peer signalled it is blocked -void possibly_remove_process(int client_id) -{ - if (client_info[client_id].stdout_fd == -1 && - client_info[client_id].stderr_fd == -1 && - client_info[client_id].is_exited) - remove_process(client_id, - client_info[client_id].exit_status); -} - - -void handle_input(int client_id, int len) -{ - char buf[len]; - - read_all_vchan_ext(buf, len); - if (!client_info[client_id].pid || client_info[client_id].stdin_fd == -1) - return; - - if (len == 0) { - if (client_info[client_id].is_blocked) - client_info[client_id].is_close_after_flush_needed - = 1; - else { - close(client_info[client_id].stdin_fd); - client_info[client_id].stdin_fd = -1; - } - return; - } - - switch (write_stdin - (client_info[client_id].stdin_fd, client_id, buf, len, - &client_info[client_id].buffer)) { - case WRITE_STDIN_OK: - break; - case WRITE_STDIN_BUFFERED: - client_info[client_id].is_blocked = 1; - break; - case WRITE_STDIN_ERROR: - // do not remove process, as it still can write data to stdout - close(client_info[client_id].stdin_fd); - client_info[client_id].stdin_fd = -1; - client_info[client_id].is_blocked = 0; - break; - default: - fprintf(stderr, "unknown write_stdin?\n"); - exit(1); - } - -} - -void set_blocked_outerr(int client_id, int val) -{ - process_fd[client_info[client_id].stdout_fd].is_blocked = val; - process_fd[client_info[client_id].stderr_fd].is_blocked = val; -} - -void handle_server_data() -{ - struct server_header s_hdr; - read_all_vchan_ext(&s_hdr, sizeof s_hdr); - -// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.client_id, -// s_hdr.len); - - switch (s_hdr.type) { - case MSG_XON: - set_blocked_outerr(s_hdr.client_id, 0); - break; - case MSG_XOFF: - set_blocked_outerr(s_hdr.client_id, 1); - break; - case MSG_SERVER_TO_AGENT_CONNECT_EXISTING: - handle_connect_existing(s_hdr.client_id, s_hdr.len); - break; - case MSG_SERVER_TO_AGENT_EXEC_CMDLINE: - wake_meminfo_writer(); - handle_exec(s_hdr.client_id, s_hdr.len); - break; - case MSG_SERVER_TO_AGENT_JUST_EXEC: - wake_meminfo_writer(); - handle_just_exec(s_hdr.client_id, s_hdr.len); - break; - case MSG_SERVER_TO_AGENT_INPUT: - handle_input(s_hdr.client_id, s_hdr.len); - break; - case MSG_SERVER_TO_AGENT_CLIENT_END: - remove_process(s_hdr.client_id, -1); - break; - default: - fprintf(stderr, "msg type from daemon is %d ?\n", - s_hdr.type); - exit(1); - } -} - -void handle_process_data(int fd) -{ - struct server_header s_hdr; - char buf[MAX_DATA_CHUNK]; - int ret; - int len; - - len = buffer_space_vchan_ext(); - if (len <= sizeof s_hdr) - return; - - ret = read(fd, buf, len - sizeof s_hdr); - s_hdr.client_id = process_fd[fd].client_id; - - if (process_fd[fd].type == FDTYPE_STDOUT) - s_hdr.type = MSG_AGENT_TO_SERVER_STDOUT; - else if (process_fd[fd].type == FDTYPE_STDERR) - s_hdr.type = MSG_AGENT_TO_SERVER_STDERR; - else { - fprintf(stderr, "fd=%d, client_id=%d, type=%d ?\n", fd, - process_fd[fd].client_id, process_fd[fd].type); - exit(1); - } - s_hdr.len = ret; - if (ret >= 0) { - write_all_vchan_ext(&s_hdr, sizeof s_hdr); - write_all_vchan_ext(buf, ret); - } - if (ret == 0) { - int client_id = process_fd[fd].client_id; - if (process_fd[fd].type == FDTYPE_STDOUT) - client_info[client_id].stdout_fd = -1; - else - client_info[client_id].stderr_fd = -1; - - process_fd[fd].type = FDTYPE_INVALID; - process_fd[fd].client_id = -1; - process_fd[fd].is_blocked = 0; - close(fd); - update_max_process_fd(); - possibly_remove_process(client_id); - } - if (ret < 0) - remove_process(process_fd[fd].client_id, 127); -} - -volatile int child_exited; - -void sigchld_handler(int x) -{ - child_exited = 1; - signal(SIGCHLD, sigchld_handler); -} - -int find_info(int pid) -{ - int i; - for (i = 0; i < MAX_FDS; i++) - if (client_info[i].pid == pid) - return i; - return -1; -} - - -void handle_process_data_all(fd_set * select_fds) -{ - int i; - for (i = 0; i <= max_process_fd; i++) - if (process_fd[i].type != FDTYPE_INVALID - && FD_ISSET(i, select_fds)) - handle_process_data(i); -} - -void reap_children() -{ - int status; - int pid; - int client_id; - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { - client_id = find_info(pid); - if (client_id < 0) - continue; - client_info[client_id].is_exited = 1; - client_info[client_id].exit_status = status; - possibly_remove_process(client_id); - } - child_exited = 0; -} - -int fill_fds_for_select(fd_set * rdset, fd_set * wrset) -{ - int max = -1; - int fd, i; - FD_ZERO(rdset); - FD_ZERO(wrset); - - for (i = 0; i <= max_process_fd; i++) - if (process_fd[i].type != FDTYPE_INVALID - && !process_fd[i].is_blocked) { - FD_SET(i, rdset); - max = i; - } - - FD_SET(trigger_fd, rdset); - if (trigger_fd > max) - max = trigger_fd; - FD_SET(passfd_socket, rdset); - if (passfd_socket > max) - max = passfd_socket; - - for (i = 0; i < MAX_FDS; i++) - if (client_info[i].pid && client_info[i].is_blocked) { - fd = client_info[i].stdin_fd; - FD_SET(fd, wrset); - if (fd > max) - max = fd; - } - return max; -} - -void flush_client_data_agent(int client_id) -{ - struct _client_info *info = &client_info[client_id]; - switch (flush_client_data - (info->stdin_fd, client_id, &info->buffer)) { - case WRITE_STDIN_OK: - info->is_blocked = 0; - if (info->is_close_after_flush_needed) { - close(info->stdin_fd); - info->stdin_fd = -1; - info->is_close_after_flush_needed = 0; - } - break; - case WRITE_STDIN_ERROR: - // do not remove process, as it still can write data to stdout - info->is_blocked = 0; - close(info->stdin_fd); - info->stdin_fd = -1; - info->is_close_after_flush_needed = 0; - break; - case WRITE_STDIN_BUFFERED: - break; - default: - fprintf(stderr, "unknown flush_client_data?\n"); - exit(1); - } -} - -void handle_new_passfd() -{ - int fd = do_accept(passfd_socket); - if (fd >= MAX_FDS) { - fprintf(stderr, "too many clients ?\n"); - exit(1); - } - // let client know what fd has been allocated - write(fd, &fd, sizeof(fd)); -} - - -void handle_trigger_io() -{ - struct server_header s_hdr; - struct trigger_connect_params params; - int ret; - - s_hdr.client_id = 0; - s_hdr.len = 0; - ret = read(trigger_fd, ¶ms, sizeof(params)); - if (ret == sizeof(params)) { - s_hdr.type = MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING; - write_all_vchan_ext(&s_hdr, sizeof s_hdr); - write_all_vchan_ext(¶ms, sizeof params); - } -// trigger_fd is nonblock - so no need to reopen -// not really, need to reopen at EOF - if (ret <= 0) { - close(trigger_fd); - trigger_fd = - open(QREXEC_AGENT_TRIGGER_PATH, O_RDONLY | O_NONBLOCK); - } -} - -int main() -{ - fd_set rdset, wrset; - int max; - int i; - sigset_t chld_set; - - init(); - signal(SIGCHLD, sigchld_handler); - signal(SIGPIPE, SIG_IGN); - sigemptyset(&chld_set); - sigaddset(&chld_set, SIGCHLD); - - - for (;;) { - sigprocmask(SIG_BLOCK, &chld_set, NULL); - if (child_exited) - reap_children(); - max = fill_fds_for_select(&rdset, &wrset); - if (buffer_space_vchan_ext() <= - sizeof(struct server_header)) - FD_ZERO(&rdset); - - wait_for_vchan_or_argfd(max, &rdset, &wrset); - sigprocmask(SIG_UNBLOCK, &chld_set, NULL); - - if (FD_ISSET(passfd_socket, &rdset)) - handle_new_passfd(); - - while (read_ready_vchan_ext()) - handle_server_data(); - - if (FD_ISSET(trigger_fd, &rdset)) - handle_trigger_io(); - - handle_process_data_all(&rdset); - for (i = 0; i <= MAX_FDS; i++) - if (client_info[i].pid - && client_info[i].is_blocked - && FD_ISSET(client_info[i].stdin_fd, &wrset)) - flush_client_data_agent(i); - } -} diff --git a/qrexec/qrexec_client.c b/qrexec/qrexec_client.c deleted file mode 100644 index b4d1835b..00000000 --- a/qrexec/qrexec_client.c +++ /dev/null @@ -1,295 +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 "qrexec.h" -#include "buffer.h" -#include "glue.h" - -int connect_unix_socket(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); - } - return s; -} - -void do_exec(char *prog) -{ - execl("/bin/bash", "bash", "-c", prog, NULL); -} - -int local_stdin_fd, local_stdout_fd; - -void do_exit(int code) -{ - int status; -// sever communication lines; wait for child, if any -// so that qrexec-daemon can count (recursively) spawned processes correctly - close(local_stdin_fd); - close(local_stdout_fd); - waitpid(-1, &status, 0); - exit(code); -} - - -void prepare_local_fds(char *cmdline) -{ - int pid; - if (!cmdline) { - local_stdin_fd = 1; - local_stdout_fd = 0; - return; - } - do_fork_exec(cmdline, &pid, &local_stdin_fd, &local_stdout_fd, - NULL); -} - - -void send_cmdline(int s, int type, char *cmdline) -{ - struct client_header hdr; - hdr.type = type; - hdr.len = strlen(cmdline) + 1; - if (!write_all(s, &hdr, sizeof(hdr)) - || !write_all(s, cmdline, hdr.len)) { - perror("write daemon"); - do_exit(1); - } -} - -void handle_input(int s) -{ - char buf[MAX_DATA_CHUNK]; - int ret; - ret = read(local_stdout_fd, buf, sizeof(buf)); - if (ret < 0) { - perror("read"); - do_exit(1); - } - if (ret == 0) { - close(local_stdout_fd); - local_stdout_fd = -1; - shutdown(s, SHUT_WR); - if (local_stdin_fd == -1) { - // if pipe in opposite direction already closed, no need to stay alive - do_exit(0); - } - } - if (!write_all(s, buf, ret)) { - if (errno == EPIPE) { - // daemon disconnected its end of socket, so no future data will be - // send there; there is no sense to read from child stdout - // - // since AF_UNIX 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_SERVER_TO_CLIENT_EXIT_CODE message) - do_exit(1); - } - } else - perror("write daemon"); - } -} - -void handle_daemon_data(int s) -{ - int status; - struct client_header hdr; - char buf[MAX_DATA_CHUNK]; - - if (!read_all(s, &hdr, sizeof hdr)) { - perror("read daemon"); - do_exit(1); - } - if (hdr.len > MAX_DATA_CHUNK) { - fprintf(stderr, "client_header.len=%d\n", hdr.len); - do_exit(1); - } - if (!read_all(s, buf, hdr.len)) { - perror("read daemon"); - do_exit(1); - } - - switch (hdr.type) { - case MSG_SERVER_TO_CLIENT_STDOUT: - if (local_stdin_fd == -1) - break; - if (hdr.len == 0) { - close(local_stdin_fd); - local_stdin_fd = -1; - } else if (!write_all(local_stdin_fd, buf, hdr.len)) { - if (errno == EPIPE) { - // remote side have closed its stdin, handle data in oposite - // direction (if any) before exit - local_stdin_fd = -1; - } else { - perror("write local stdout"); - do_exit(1); - } - } - break; - case MSG_SERVER_TO_CLIENT_STDERR: - write_all(2, buf, hdr.len); - break; - case MSG_SERVER_TO_CLIENT_EXIT_CODE: - status = *(unsigned int *) buf; - if (WIFEXITED(status)) - do_exit(WEXITSTATUS(status)); - else - do_exit(255); - break; - default: - fprintf(stderr, "unknown msg %d\n", hdr.type); - do_exit(1); - } -} - -// perhaps we could save a syscall if we include both sides in both -// rdset and wrset; to be investigated -void handle_daemon_only_until_writable(s) -{ - fd_set rdset, wrset; - - do { - FD_ZERO(&rdset); - FD_ZERO(&wrset); - FD_SET(s, &rdset); - FD_SET(s, &wrset); - - if (select(s + 1, &rdset, &wrset, NULL, NULL) < 0) { - perror("select"); - do_exit(1); - } - if (FD_ISSET(s, &rdset)) - handle_daemon_data(s); - } while (!FD_ISSET(s, &wrset)); -} - -void select_loop(int s) -{ - fd_set select_set; - int max; - for (;;) { - handle_daemon_only_until_writable(s); - FD_ZERO(&select_set); - FD_SET(s, &select_set); - max = s; - if (local_stdout_fd != -1) { - FD_SET(local_stdout_fd, &select_set); - if (s < local_stdout_fd) - max = local_stdout_fd; - } - if (select(max + 1, &select_set, NULL, NULL, NULL) < 0) { - perror("select"); - do_exit(1); - } - if (FD_ISSET(s, &select_set)) - handle_daemon_data(s); - if (local_stdout_fd != -1 - && FD_ISSET(local_stdout_fd, &select_set)) - handle_input(s); - } -} - -void usage(char *name) -{ - fprintf(stderr, - "usage: %s -d domain_num [-l local_prog] -e -c remote_cmdline\n" - "-e means exit after sending cmd, -c: connect to existing process\n", - name); - exit(1); -} - -int main(int argc, char **argv) -{ - int opt; - char *domname = NULL; - int s; - int just_exec = 0; - int connect_existing = 0; - char *local_cmdline = NULL; - while ((opt = getopt(argc, argv, "d:l:ec")) != -1) { - switch (opt) { - case 'd': - domname = strdup(optarg); - break; - case 'l': - local_cmdline = strdup(optarg); - break; - case 'e': - just_exec = 1; - break; - case 'c': - connect_existing = 1; - break; - default: - usage(argv[0]); - } - } - if (optind >= argc || !domname) - usage(argv[0]); - - s = connect_unix_socket(domname); - setenv("QREXEC_REMOTE_DOMAIN", domname, 1); - prepare_local_fds(local_cmdline); - - if (just_exec) - send_cmdline(s, MSG_CLIENT_TO_SERVER_JUST_EXEC, - argv[optind]); - else { - int cmd; - if (connect_existing) - cmd = MSG_CLIENT_TO_SERVER_CONNECT_EXISTING; - else - cmd = MSG_CLIENT_TO_SERVER_EXEC_CMDLINE; - send_cmdline(s, cmd, argv[optind]); - select_loop(s); - } - return 0; -} diff --git a/qrexec/qrexec_client_vm.c b/qrexec/qrexec_client_vm.c deleted file mode 100644 index 4d0ae560..00000000 --- a/qrexec/qrexec_client_vm.c +++ /dev/null @@ -1,109 +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. - * - */ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include "qrexec.h" -int connect_unix_socket() -{ - 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; - strncpy(remote.sun_path, QREXEC_AGENT_FDPASS_PATH, - sizeof(remote.sun_path)); - len = strlen(remote.sun_path) + sizeof(remote.sun_family); - if (connect(s, (struct sockaddr *) &remote, len) == -1) { - perror("connect"); - exit(1); - } - return s; -} - -char *get_program_name(char *prog) -{ - char *basename = rindex(prog, '/'); - if (basename) - return basename + 1; - else - return prog; -} - -int main(int argc, char **argv) -{ - int trigger_fd; - struct trigger_connect_params params; - int local_fd[3], remote_fd[3]; - int i; - char *abs_exec_path; - - if (argc < 4) { - fprintf(stderr, - "usage: %s target_vmname program_ident local_program [local program arguments]\n", - argv[0]); - exit(1); - } - - trigger_fd = open(QREXEC_AGENT_TRIGGER_PATH, O_WRONLY); - if (trigger_fd < 0) { - perror("open QREXEC_AGENT_TRIGGER_PATH"); - exit(1); - } - - for (i = 0; i < 3; i++) { - local_fd[i] = connect_unix_socket(); - read(local_fd[i], &remote_fd[i], sizeof(remote_fd[i])); - if (i != 2 || getenv("PASS_LOCAL_STDERR")) { - char *env; - asprintf(&env, "SAVED_FD_%d=%d", i, dup(i)); - putenv(env); - dup2(local_fd[i], i); - close(local_fd[i]); - } - } - - memset(¶ms, 0, sizeof(params)); - strncpy(params.exec_index, argv[2], sizeof(params.exec_index)); - strncpy(params.target_vmname, argv[1], - sizeof(params.target_vmname)); - snprintf(params.process_fds.ident, - sizeof(params.process_fds.ident), "%d %d %d", - remote_fd[0], remote_fd[1], remote_fd[2]); - - write(trigger_fd, ¶ms, sizeof(params)); - close(trigger_fd); - - abs_exec_path = strdup(argv[3]); - argv[3] = get_program_name(argv[3]); - execv(abs_exec_path, argv + 3); - perror("execv"); - return 1; -} diff --git a/qrexec/qrexec_daemon.c b/qrexec/qrexec_daemon.c deleted file mode 100644 index 6ef86bd3..00000000 --- a/qrexec/qrexec_daemon.c +++ /dev/null @@ -1,687 +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 "buffer.h" -#include "glue.h" - -enum client_flags { - CLIENT_INVALID = 0, // table slot not used - CLIENT_CMDLINE = 1, // waiting for cmdline from client - CLIENT_DATA = 2, // waiting for data from client - CLIENT_DONT_READ = 4, // don't read from the client, the other side pipe is full, or EOF (additionally marked with CLIENT_EOF) - CLIENT_OUTQ_FULL = 8, // don't write to client, its stdin pipe is full - CLIENT_EOF = 16, // got EOF - CLIENT_EXITED = 32 // only send remaining data from client and remove from list -}; - -struct _client { - int state; // combination of above enum client_flags - struct buffer buffer; // buffered data to client, if any -}; - -/* -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 - -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 -char *default_user = "user"; -char default_user_keyword[] = "DEFAULT:"; -#define default_user_keyword_len_without_colon (sizeof(default_user_keyword)-2) - -void sigusr1_handler(int x) -{ - fprintf(stderr, "connected\n"); - exit(0); -} - -void sigchld_handler(int x); - -char *remote_domain_name; // guess what - -int create_qrexec_socket(int domid, 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); - symlink(socket_address, link_to_socket_name); - return get_server_socket(socket_address); -} - -#define MAX_STARTUP_TIME_DEFAULT 60 - -/* ask on qrexec connect timeout */ -int ask_on_connect_timeout(int xid, int timeout) -{ - char text[1024]; - int ret; - struct stat buf; - ret=stat("/usr/bin/kdialog", &buf); -#define KDIALOG_CMD "kdialog --title 'Qrexec daemon' --warningyesno " -#define ZENITY_CMD "zenity --title 'Qrexec daemon' --question --text " - snprintf(text, sizeof(text), - "%s" - "'Timeout while trying connecting to qrexec agent (Xen domain ID: %d). Do you want to wait next %d seconds?'", - ret==0 ? KDIALOG_CMD : ZENITY_CMD, - xid, timeout); -#undef KDIALOG_CMD -#undef ZENITY_CMD - ret = system(text); - ret = WEXITSTATUS(ret); - // fprintf(stderr, "ret=%d\n", ret); - switch (ret) { - case 1: /* NO */ - return 0; - case 0: /*YES */ - return 1; - default: - // this can be the case at system startup (netvm), when Xorg isn't running yet - // so just don't give possibility to extend the timeout - 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; - 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 number - startup_timeout = MAX_STARTUP_TIME_DEFAULT; - } - signal(SIGUSR1, sigusr1_handler); - switch (pid=fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - break; - default: - fprintf(stderr, "Waiting for VM's qrexec agent."); - for (i=0;i= MAX_CLIENTS) { - fprintf(stderr, "too many clients ?\n"); - exit(1); - } - clients[fd].state = CLIENT_CMDLINE; - buffer_init(&clients[fd].buffer); - if (fd > max_client_fd) - max_client_fd = fd; -} - -/* -we need to track the number of children, so that excessive QREXEC_EXECUTE_* -commands do not fork-bomb dom0 -*/ -int children_count; - -void terminate_client_and_flush_data(int fd) -{ - int i; - struct server_header s_hdr; - - if (!(clients[fd].state & CLIENT_EXITED) && fork_and_flush_stdin(fd, &clients[fd].buffer)) - children_count++; - close(fd); - clients[fd].state = CLIENT_INVALID; - buffer_free(&clients[fd].buffer); - if (max_client_fd == fd) { - for (i = fd; clients[i].state == CLIENT_INVALID && i >= 0; - i--); - max_client_fd = i; - } - s_hdr.type = MSG_SERVER_TO_AGENT_CLIENT_END; - s_hdr.client_id = fd; - s_hdr.len = 0; - write_all_vchan_ext(&s_hdr, sizeof(s_hdr)); -} - -int get_cmdline_body_from_client_and_pass_to_agent(int fd, struct server_header - *s_hdr) -{ - int len = s_hdr->len; - char buf[len]; - int use_default_user = 0; - if (!read_all(fd, buf, len)) { - terminate_client_and_flush_data(fd); - return 0; - } - if (!strncmp(buf, default_user_keyword, default_user_keyword_len_without_colon+1)) { - use_default_user = 1; - s_hdr->len -= default_user_keyword_len_without_colon; // -1 because of colon - s_hdr->len += strlen(default_user); - } - write_all_vchan_ext(s_hdr, sizeof(*s_hdr)); - if (use_default_user) { - write_all_vchan_ext(default_user, strlen(default_user)); - write_all_vchan_ext(buf+default_user_keyword_len_without_colon, len-default_user_keyword_len_without_colon); - } else - write_all_vchan_ext(buf, len); - return 1; -} - -void handle_cmdline_message_from_client(int fd) -{ - struct client_header hdr; - struct server_header s_hdr; - if (!read_all(fd, &hdr, sizeof hdr)) { - terminate_client_and_flush_data(fd); - return; - } - switch (hdr.type) { - case MSG_CLIENT_TO_SERVER_EXEC_CMDLINE: - s_hdr.type = MSG_SERVER_TO_AGENT_EXEC_CMDLINE; - break; - case MSG_CLIENT_TO_SERVER_JUST_EXEC: - s_hdr.type = MSG_SERVER_TO_AGENT_JUST_EXEC; - break; - case MSG_CLIENT_TO_SERVER_CONNECT_EXISTING: - s_hdr.type = MSG_SERVER_TO_AGENT_CONNECT_EXISTING; - break; - default: - terminate_client_and_flush_data(fd); - return; - } - - s_hdr.client_id = fd; - s_hdr.len = hdr.len; - if (!get_cmdline_body_from_client_and_pass_to_agent(fd, &s_hdr)) - // client disconnected while sending cmdline, above call already - // cleaned up client info - return; - clients[fd].state = CLIENT_DATA; - set_nonblock(fd); // so that we can detect full queue without blocking - if (hdr.type == MSG_CLIENT_TO_SERVER_JUST_EXEC) - terminate_client_and_flush_data(fd); - -} - -/* handle data received from one of qrexec_client processes */ -void handle_message_from_client(int fd) -{ - struct server_header s_hdr; - char buf[MAX_DATA_CHUNK]; - int len, ret; - - if (clients[fd].state == CLIENT_CMDLINE) { - handle_cmdline_message_from_client(fd); - return; - } - // We have already passed cmdline from client. - // Now the client passes us raw data from its stdin. - len = buffer_space_vchan_ext(); - if (len <= sizeof s_hdr) - return; - /* Read at most the amount of data that we have room for in vchan */ - ret = read(fd, buf, len - sizeof(s_hdr)); - if (ret < 0) { - perror("read client"); - terminate_client_and_flush_data(fd); - return; - } - s_hdr.client_id = fd; - s_hdr.len = ret; - s_hdr.type = MSG_SERVER_TO_AGENT_INPUT; - - write_all_vchan_ext(&s_hdr, sizeof(s_hdr)); - write_all_vchan_ext(buf, ret); - if (ret == 0) // EOF - so don't select() on this client - clients[fd].state |= CLIENT_DONT_READ | CLIENT_EOF; - if (clients[fd].state & CLIENT_EXITED) - //client already exited and all data sent - cleanup now - terminate_client_and_flush_data(fd); -} - -/* -Called when there is buffered data for this client, and select() reports -that client's pipe is writable; so we should be able to flush some -buffered data. -*/ -void write_buffered_data_to_client(int client_id) -{ - switch (flush_client_data - (client_id, client_id, &clients[client_id].buffer)) { - case WRITE_STDIN_OK: // no more buffered data - clients[client_id].state &= ~CLIENT_OUTQ_FULL; - break; - case WRITE_STDIN_ERROR: - // do not write to this fd anymore - clients[client_id].state |= CLIENT_EXITED; - if (clients[client_id].state & CLIENT_EOF) - terminate_client_and_flush_data(client_id); - else - // client will be removed when read returns 0 (EOF) - // clear CLIENT_OUTQ_FULL flag to no select on this fd anymore - clients[client_id].state &= ~CLIENT_OUTQ_FULL; - break; - case WRITE_STDIN_BUFFERED: // no room for all data, don't clear CLIENT_OUTQ_FULL flag - break; - default: - fprintf(stderr, "unknown flush_client_data?\n"); - exit(1); - } -} - -/* -The header (hdr argument) is already built. Just read the raw data from -the packet, and pass it along with the header to the client. -*/ -void get_packet_data_from_agent_and_pass_to_client(int client_id, struct client_header - *hdr) -{ - int len = hdr->len; - char buf[sizeof(*hdr) + len]; - - /* make both the header and data be consecutive in the buffer */ - *(struct client_header *) buf = *hdr; - read_all_vchan_ext(buf + sizeof(*hdr), len); - if (clients[client_id].state & CLIENT_EXITED) - // ignore data for no longer running client - return; - - switch (write_stdin - (client_id, client_id, buf, len + sizeof(*hdr), - &clients[client_id].buffer)) { - case WRITE_STDIN_OK: - break; - case WRITE_STDIN_BUFFERED: // some data have been buffered - clients[client_id].state |= CLIENT_OUTQ_FULL; - break; - case WRITE_STDIN_ERROR: - // do not write to this fd anymore - clients[client_id].state |= CLIENT_EXITED; - // if already got EOF, remove client - if (clients[client_id].state & CLIENT_EOF) - terminate_client_and_flush_data(client_id); - break; - default: - fprintf(stderr, "unknown write_stdin?\n"); - 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; - -void sigchld_handler(int x) -{ - child_exited = 1; - signal(SIGCHLD, sigchld_handler); -} - -/* clean zombies, update children_count */ -void reap_children() -{ - int status; - while (waitpid(-1, &status, WNOHANG) > 0) - children_count--; - child_exited = 0; -} - -/* too many children - wait for one of them to terminate */ -void wait_for_child() -{ - int status; - waitpid(-1, &status, 0); - children_count--; -} - -#define MAX_CHILDREN 10 -void check_children_count_and_wait_if_too_many() -{ - if (children_count > MAX_CHILDREN) { - fprintf(stderr, - "max number of children reached, waiting for child exit...\n"); - wait_for_child(); - fprintf(stderr, "now children_count=%d, continuing.\n", - children_count); - } -} - -void sanitize_name(char * untrusted_s_signed) -{ - 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 == '-' || *untrusted_s == '.' || *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. -*/ - -void handle_execute_predefined_command() -{ - int i; - struct trigger_connect_params untrusted_params, params; - - check_children_count_and_wait_if_too_many(); - read_all_vchan_ext(&untrusted_params, sizeof(params)); - - /* sanitize start */ - ENSURE_NULL_TERMINATED(untrusted_params.exec_index); - ENSURE_NULL_TERMINATED(untrusted_params.target_vmname); - ENSURE_NULL_TERMINATED(untrusted_params.process_fds.ident); - sanitize_name(untrusted_params.exec_index); - sanitize_name(untrusted_params.target_vmname); - sanitize_name(untrusted_params.process_fds.ident); - params = untrusted_params; - /* sanitize end */ - - switch (fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - break; - default: - children_count++; - return; - } - for (i = 3; i < MAX_FDS; i++) - close(i); - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); - execl("/usr/lib/qubes/qrexec_policy", "qrexec_policy", - remote_domain_name, params.target_vmname, - params.exec_index, params.process_fds.ident, NULL); - perror("execl"); - exit(1); -} - -void check_client_id_in_range(unsigned int untrusted_client_id) -{ - if (untrusted_client_id >= MAX_CLIENTS || untrusted_client_id < 0) { - fprintf(stderr, "from agent: client_id=%d\n", - untrusted_client_id); - exit(1); - } -} - - -void sanitize_message_from_agent(struct server_header *untrusted_header) -{ - switch (untrusted_header->type) { - case MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING: - break; - case MSG_AGENT_TO_SERVER_STDOUT: - case MSG_AGENT_TO_SERVER_STDERR: - case MSG_AGENT_TO_SERVER_EXIT_CODE: - check_client_id_in_range(untrusted_header->client_id); - if (untrusted_header->len > MAX_DATA_CHUNK - || untrusted_header->len < 0) { - fprintf(stderr, "agent feeded %d of data bytes?\n", - untrusted_header->len); - exit(1); - } - break; - - case MSG_XOFF: - case MSG_XON: - check_client_id_in_range(untrusted_header->client_id); - break; - default: - fprintf(stderr, "unknown mesage type %d from agent\n", - untrusted_header->type); - exit(1); - } -} - -void handle_message_from_agent() -{ - struct client_header hdr; - struct server_header s_hdr, untrusted_s_hdr; - - read_all_vchan_ext(&untrusted_s_hdr, sizeof untrusted_s_hdr); - /* sanitize start */ - sanitize_message_from_agent(&untrusted_s_hdr); - s_hdr = untrusted_s_hdr; - /* sanitize end */ - -// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.client_id, -// s_hdr.len); - - if (s_hdr.type == MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING) { - handle_execute_predefined_command(); - return; - } - - if (s_hdr.type == MSG_XOFF) { - clients[s_hdr.client_id].state |= CLIENT_DONT_READ; - return; - } - - if (s_hdr.type == MSG_XON) { - clients[s_hdr.client_id].state &= ~CLIENT_DONT_READ; - return; - } - - switch (s_hdr.type) { - case MSG_AGENT_TO_SERVER_STDOUT: - hdr.type = MSG_SERVER_TO_CLIENT_STDOUT; - break; - case MSG_AGENT_TO_SERVER_STDERR: - hdr.type = MSG_SERVER_TO_CLIENT_STDERR; - break; - case MSG_AGENT_TO_SERVER_EXIT_CODE: - hdr.type = MSG_SERVER_TO_CLIENT_EXIT_CODE; - break; - default: /* cannot happen, already sanitized */ - fprintf(stderr, "from agent: type=%d\n", s_hdr.type); - exit(1); - } - hdr.len = s_hdr.len; - if (clients[s_hdr.client_id].state == CLIENT_INVALID) { - // benefit of doubt - maybe client exited earlier - // just eat the packet data and continue - char buf[MAX_DATA_CHUNK]; - read_all_vchan_ext(buf, s_hdr.len); - return; - } - get_packet_data_from_agent_and_pass_to_client(s_hdr.client_id, - &hdr); - if (s_hdr.type == MSG_AGENT_TO_SERVER_EXIT_CODE) - terminate_client_and_flush_data(s_hdr.client_id); -} - -/* -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. -*/ -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 - && !(clients[i].state & CLIENT_DONT_READ)) { - FD_SET(i, read_fdset); - max = i; - } - if (clients[i].state != CLIENT_INVALID - && clients[i].state & CLIENT_OUTQ_FULL) { - FD_SET(i, write_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; - int max; - sigset_t chld_set; - - if (argc != 2 && argc != 3) { - fprintf(stderr, "usage: %s domainid [default user]\n", argv[0]); - exit(1); - } - if (argc == 3) - default_user = argv[2]; - init(atoi(argv[1])); - sigemptyset(&chld_set); - sigaddset(&chld_set, SIGCHLD); - /* - 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 (buffer_space_vchan_ext() <= - sizeof(struct server_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(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 (read_ready_vchan_ext()) - 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); - - for (i = 0; i <= max_client_fd; i++) - if (clients[i].state != CLIENT_INVALID - && FD_ISSET(i, &write_fdset)) - write_buffered_data_to_client(i); - - } -} diff --git a/qrexec/qrexec_policy b/qrexec/qrexec_policy deleted file mode 100755 index c72aaee2..00000000 --- a/qrexec/qrexec_policy +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/python -import sys -import os -import os.path -import subprocess -import xen.lowlevel.xl -import qubes.guihelpers -from optparse import OptionParser -import fcntl - -POLICY_FILE_DIR="/etc/qubes_rpc/policy" -QREXEC_CLIENT="/usr/lib/qubes/qrexec_client" - -class UserChoice: - ALLOW=0 - DENY=1 - ALWAYS_ALLOW=2 - -def line_to_dict(line): - tokens=line.split() - if len(tokens) < 3: - return None - - if tokens[0][0] == '#': - return None - - dict={} - dict['source']=tokens[0] - dict['dest']=tokens[1] - - dict['full-action']=tokens[2] - action_list=tokens[2].split(',') - dict['action']=action_list.pop(0) - - for iter in action_list: - paramval=iter.split("=") - dict["action."+paramval[0]]=paramval[1] - - return dict - - -def read_policy_file(exec_index): - policy_file=POLICY_FILE_DIR+"/"+exec_index - if not os.path.isfile(policy_file): - return None - policy_list=list() - f = open(policy_file) - fcntl.flock(f, fcntl.LOCK_SH) - for iter in f.readlines(): - dict = line_to_dict(iter) - if dict is not None: - policy_list.append(dict) - f.close() - return policy_list - -def is_match(item, config_term): - return (item is not "dom0" and config_term == "$anyvm") or item == config_term - -def get_default_policy(): - dict={} - dict["action"]="deny" - return dict - - -def find_policy(policy, domain, target): - for iter in policy: - if not is_match(domain, iter["source"]): - continue - if not is_match(target, iter["dest"]): - continue - return iter - return get_default_policy() - -def is_domain_running(target): - xl_ctx = xen.lowlevel.xl.ctx() - domains = xl_ctx.list_domains() - for dominfo in domains: - domname = xl_ctx.domid_to_name(dominfo.domid) - if domname == target: - return True - return False - -def spawn_target_if_necessary(target): - if is_domain_running(target): - return - null=open("/dev/null", "r+") - subprocess.call(["qvm-run", "-a", "-q", target, "true"], stdin=null, stdout=null) - null.close() - -def do_execute(domain, target, user, exec_index, process_ident): - if target == "dom0": - cmd="/usr/lib/qubes/qubes_rpc_multiplexer "+exec_index + " " + domain - elif target == "$dispvm": - cmd = "/usr/lib/qubes/qfile-daemon-dvm " + exec_index + " " + domain + " " +user - else: - # see the previous commit why "qvm-run -a" is broken and dangerous - # also, dangling "xl" would keep stderr open and may prevent closing connection - spawn_target_if_necessary(target) - cmd= QREXEC_CLIENT + " -d " + target + " '" + user - cmd+=":QUBESRPC "+ exec_index + " " + domain + "'" - os.execl(QREXEC_CLIENT, "qrexec_client", "-d", domain, "-l", cmd, "-c", process_ident) - -def confirm_execution(domain, target, exec_index): - text = "Do you allow domain \"" +domain + "\" to execute " + exec_index - text+= " operation on the domain \"" + target +"\"?
" - text+= " \"Yes to All\" option will automatically allow this operation in the future." - return qubes.guihelpers.ask(text, yestoall=True) - -def add_always_allow(domain, target, exec_index, options): - policy_file=POLICY_FILE_DIR+"/"+exec_index - if not os.path.isfile(policy_file): - return None - f = open(policy_file, 'r+') - fcntl.flock(f, fcntl.LOCK_EX) - lines = [] - for l in f.readlines(): - lines.append(l) - lines.insert(0, "%s\t%s\tallow%s\n" % (domain, target, options)) - f.seek(0) - f.write("".join(lines)) - f.close() - -def policy_editor(domain, target, exec_index): - text = "No policy definition found for " + exec_index + " action. " - text+= "Please create a policy file in Dom0 in " + "/etc/qubes_rpc/policy/" + exec_index - subprocess.call(["/usr/bin/zenity", "--info", "--text", text]) - -def main(): - usage = "usage: %prog [options] " - parser = OptionParser (usage) - parser.add_option ("--assume-yes-for-ask", action="store_true", dest="assume_yes_for_ask", default=False, - help="Allow run of service without confirmation if policy say 'ask'") - parser.add_option ("--just-evaluate", action="store_true", dest="just_evaluate", default=False, - help="Do not run the service, only evaluate policy; retcode=0 means 'allow'") - - (options, args) = parser.parse_args () - domain=args[0] - target=args[1] - exec_index=args[2] - process_ident=args[3] - - policy_list=read_policy_file(exec_index) - if policy_list==None: - policy_editor(domain, target, exec_index) - policy_list=read_policy_file(exec_index) - if policy_list==None: - policy_list=list() - - policy_dict=find_policy(policy_list, domain, target) - - if policy_dict["action"] == "ask" and options.assume_yes_for_ask: - policy_dict["action"] = "allow" - - if policy_dict["action"] == "ask": - user_choice = confirm_execution(domain, target, exec_index) - if user_choice == UserChoice.ALWAYS_ALLOW: - add_always_allow(domain, target, exec_index, policy_dict["full-action"].lstrip('ask')) - policy_dict["action"] = "allow" - elif user_choice == UserChoice.ALLOW: - policy_dict["action"] = "allow" - else: - policy_dict["action"] = "deny" - - if options.just_evaluate: - if policy_dict["action"] == "allow": - exit(0) - else: - exit(1) - - if policy_dict["action"] == "allow": - if policy_dict.has_key("action.target"): - target=policy_dict["action.target"] - if policy_dict.has_key("action.user"): - user=policy_dict["action.user"] - else: - user="DEFAULT" - do_execute(domain, target, user, exec_index, process_ident) - - print >> sys.stderr, "Rpc denied:", domain, target, exec_index - os.execl(QREXEC_CLIENT, "qrexec_client", "-d", domain, "-l", "/bin/false", "-c", process_ident) - -main() diff --git a/qrexec/qubes_rpc_multiplexer b/qrexec/qubes_rpc_multiplexer deleted file mode 100755 index 191be91b..00000000 --- a/qrexec/qubes_rpc_multiplexer +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh -QUBES_RPC=/etc/qubes_rpc -if ! [ $# = 2 ] ; then - echo $0: bad argument count >&2 - exit 1 -fi -CFG_FILE=$QUBES_RPC/"$1" -export QREXEC_REMOTE_DOMAIN="$2" -if [ -s "$CFG_FILE" ] ; then - exec /bin/sh "$CFG_FILE" - echo "$0: failed to execute handler for" "$1" >&2 - exit 1 -fi -echo "$0: nonexistent or empty" "$CFG_FILE" file >&2 -exit 1 diff --git a/qrexec/txrx-vchan.c b/qrexec/txrx-vchan.c deleted file mode 100644 index 61b04f87..00000000 --- a/qrexec/txrx-vchan.c +++ /dev/null @@ -1,221 +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 - -static struct libvchan *ctrl; -static int is_server; -int write_all_vchan_ext(void *buf, int size) -{ - int written = 0; - int ret; - - while (written < size) { - ret = - libvchan_write(ctrl, (char *) buf + written, - size - written); - if (ret <= 0) { - perror("write"); - exit(1); - } - written += ret; - } -// fprintf(stderr, "sent %d bytes\n", size); - return size; -} - - -int read_all_vchan_ext(void *buf, int size) -{ - int written = 0; - int ret; - while (written < size) { - ret = - libvchan_read(ctrl, (char *) buf + written, - size - written); - if (ret == 0) { - fprintf(stderr, "EOF\n"); - exit(1); - } - if (ret < 0) { - perror("read"); - exit(1); - } - written += ret; - } -// fprintf(stderr, "read %d bytes\n", size); - return size; -} - -int read_ready_vchan_ext() -{ - return libvchan_data_ready(ctrl); -} - -int buffer_space_vchan_ext() -{ - return libvchan_buffer_space(ctrl); -} - -// if the remote domain is destroyed, we get no notification -// thus, we check for the status periodically - -#ifdef XENCTRL_HAS_XC_INTERFACE -static xc_interface *xc_handle = NULL; -#else -static int xc_handle = -1; -#endif -void slow_check_for_libvchan_is_eof(struct libvchan *ctrl) -{ - struct evtchn_status evst; - evst.port = ctrl->evport; - evst.dom = DOMID_SELF; - if (xc_evtchn_status(xc_handle, &evst)) { - perror("xc_evtchn_status"); - exit(1); - } - if (evst.status != EVTCHNSTAT_interdomain) { - fprintf(stderr, "event channel disconnected\n"); - exit(0); - } -} - - -int wait_for_vchan_or_argfd_once(int max, fd_set * rdset, fd_set * wrset) -{ - int vfd, ret; - struct timespec tv = { 1, 100000000 }; - sigset_t empty_set; - - sigemptyset(&empty_set); - - vfd = libvchan_fd_for_select(ctrl); - FD_SET(vfd, rdset); - if (vfd > max) - max = vfd; - max++; - ret = pselect(max, rdset, wrset, NULL, &tv, &empty_set); - if (ret < 0) { - if (errno != EINTR) { - perror("select"); - exit(1); - } else { - FD_ZERO(rdset); - FD_ZERO(wrset); - fprintf(stderr, "eintr\n"); - return 1; - } - - } - if (libvchan_is_eof(ctrl)) { - fprintf(stderr, "libvchan_is_eof\n"); - exit(0); - } - if (!is_server && ret == 0) - slow_check_for_libvchan_is_eof(ctrl); - if (FD_ISSET(vfd, rdset)) - // the following will never block; we need to do this to - // clear libvchan_fd pending state - libvchan_wait(ctrl); - return ret; -} - -void wait_for_vchan_or_argfd(int max, fd_set * rdset, fd_set * wrset) -{ - fd_set r = *rdset, w = *wrset; - do { - *rdset = r; - *wrset = w; - } - while (wait_for_vchan_or_argfd_once(max, rdset, wrset) == 0); -} - -int peer_server_init(int port) -{ - is_server = 1; - ctrl = libvchan_server_init(port); - if (!ctrl) { - perror("libvchan_server_init"); - exit(1); - } - return 0; -} - -char *peer_client_init(int dom, int port) -{ - struct xs_handle *xs; - char buf[64]; - char *name; - char *dummy; - unsigned int len = 0; - char devbuf[128]; - unsigned int count; - char **vec; - -// double_buffered = 1; // writes to vchan are buffered, nonblocking -// double_buffer_init(); - xs = xs_daemon_open(); - if (!xs) { - perror("xs_daemon_open"); - exit(1); - } - snprintf(buf, sizeof(buf), "/local/domain/%d/name", dom); - name = xs_read(xs, 0, buf, &len); - if (!name) { - perror("xs_read domainname"); - exit(1); - } - snprintf(devbuf, sizeof(devbuf), - "/local/domain/%d/device/vchan/%d/event-channel", dom, - port); - xs_watch(xs, devbuf, devbuf); - do { - vec = xs_read_watch(xs, &count); - if (vec) - free(vec); - len = 0; - dummy = xs_read(xs, 0, devbuf, &len); - } - while (!dummy || !len); // wait for the server to create xenstore entries - free(dummy); - xs_daemon_close(xs); - - // now client init should succeed; "while" is redundant - while (!(ctrl = libvchan_client_init(dom, port))); - -#ifdef XENCTRL_HAS_XC_INTERFACE - xc_handle = xc_interface_open(NULL, 0, 0); - if (!xc_handle) { -#else - xc_handle = xc_interface_open(); - if (xc_handle < 0) { -#endif - perror("xc_interface_open"); - exit(1); - } - return name; -} diff --git a/qrexec/unix_server.c b/qrexec/unix_server.c deleted file mode 100644 index c002784c..00000000 --- a/qrexec/unix_server.c +++ /dev/null @@ -1,68 +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 "qrexec.h" - -int get_server_socket(char *socket_address) -{ - struct sockaddr_un sockname; - int s; - - unlink(socket_address); - - s = socket(AF_UNIX, SOCK_STREAM, 0); - memset(&sockname, 0, sizeof(sockname)); - sockname.sun_family = AF_UNIX; - memcpy(sockname.sun_path, socket_address, strlen(socket_address)); - - if (bind(s, (struct sockaddr *) &sockname, sizeof(sockname)) == -1) { - printf("bind() failed\n"); - close(s); - exit(1); - } -// chmod(sockname.sun_path, 0666); - if (listen(s, 5) == -1) { - perror("listen() failed\n"); - close(s); - exit(1); - } - return s; -} - -int do_accept(int s) -{ - struct sockaddr_un peer; - unsigned int addrlen; - int fd; - addrlen = sizeof(peer); - fd = accept(s, (struct sockaddr *) &peer, &addrlen); - if (fd == -1) { - perror("unix accept"); - exit(1); - } - return fd; -} diff --git a/qrexec/write_stdin.c b/qrexec/write_stdin.c deleted file mode 100644 index 939b72ea..00000000 --- a/qrexec/write_stdin.c +++ /dev/null @@ -1,137 +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 "qrexec.h" -#include "buffer.h" -#include "glue.h" - -/* -There is buffered data in "buffer" for client id "client_id", and select() -reports that "fd" is writable. Write as much as possible to fd, if all sent, -notify the peer that this client's pipe is no longer full. -*/ -int flush_client_data(int fd, int client_id, struct buffer *buffer) -{ - int ret; - int len; - for (;;) { - len = buffer_len(buffer); - if (len > MAX_DATA_CHUNK) - len = MAX_DATA_CHUNK; - ret = write(fd, buffer_data(buffer), len); - if (ret == -1) { - if (errno != EAGAIN) { - return WRITE_STDIN_ERROR; - } else - return WRITE_STDIN_BUFFERED; - } - // we previously called buffer_remove(buffer, len) - // it will be wrong if we change MAX_DATA_CHUNK to something large - // as pipes writes are atomic only to PIPE_MAX limit - buffer_remove(buffer, ret); - len = buffer_len(buffer); - if (!len) { - struct server_header s_hdr; - s_hdr.type = MSG_XON; - s_hdr.client_id = client_id; - s_hdr.len = 0; - write_all_vchan_ext(&s_hdr, sizeof s_hdr); - return WRITE_STDIN_OK; - } - } - -} - -/* -Write "len" bytes from "data" to "fd". If not all written, buffer the rest -to "buffer", and notify the peer that the client "client_id" pipe is full via -MSG_XOFF message. -*/ -int write_stdin(int fd, int client_id, char *data, int len, - struct buffer *buffer) -{ - int ret; - int written = 0; - - if (buffer_len(buffer)) { - buffer_append(buffer, data, len); - return WRITE_STDIN_BUFFERED; - } - while (written < len) { - ret = write(fd, data + written, len - written); - if (ret == 0) { - perror("write_stdin: write returns 0 ???"); - exit(1); - } - if (ret == -1) { - struct server_header s_hdr; - - if (errno != EAGAIN) - return WRITE_STDIN_ERROR; - - buffer_append(buffer, data + written, - len - written); - - s_hdr.type = MSG_XOFF; - s_hdr.client_id = client_id; - s_hdr.len = 0; - write_all_vchan_ext(&s_hdr, sizeof s_hdr); - - return WRITE_STDIN_BUFFERED; - } - written += ret; - } - return WRITE_STDIN_OK; - -} - -/* -Data feed process has exited, so we need to clear all control structures for -the client. However, if we have buffered data for the client (which is rare btw), -fire&forget a separate process to flush them. -*/ -int fork_and_flush_stdin(int fd, struct buffer *buffer) -{ - int i; - if (!buffer_len(buffer)) - return 0; - switch (fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - break; - default: - return 1; - } - for (i = 0; i < MAX_FDS; i++) - if (i != fd && i != 2) - close(i); - set_block(fd); - write_all(fd, buffer_data(buffer), buffer_len(buffer)); - exit(0); -} diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 31de9db8..37ef16c8 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -56,7 +56,7 @@ Requires(preun): systemd-units Requires(postun): systemd-units %endif Requires: python, xen-runtime, pciutils, python-inotify, python-daemon, kernel-qubes-dom0 -Requires: qubes-core-libs +Requires: qubes-qrexec-dom0 Requires: python-lxml Conflicts: qubes-gui-dom0 < 1.1.13 Requires: xen >= 4.1.0-2 @@ -86,8 +86,6 @@ python -O -m compileall dom0/qvm-core dom0/qmemman for dir in dom0/restore dom0/qubes_rpc dom0/qmemman; do (cd $dir; make) done -(cd vchan; make -f Makefile.linux) -(cd qrexec; make) %install @@ -152,9 +150,6 @@ cp aux-tools/startup-misc.sh $RPM_BUILD_ROOT/usr/lib/qubes cp aux-tools/prepare_volatile_img.sh $RPM_BUILD_ROOT/usr/lib/qubes cp qmemman/server.py $RPM_BUILD_ROOT/usr/lib/qubes/qmemman_daemon.py cp qmemman/meminfo-writer $RPM_BUILD_ROOT/usr/lib/qubes/ -cp ../qrexec/qrexec_daemon $RPM_BUILD_ROOT/usr/lib/qubes/ -cp ../qrexec/qrexec_client $RPM_BUILD_ROOT/usr/lib/qubes/ -cp ../qrexec/qrexec_policy $RPM_BUILD_ROOT/usr/lib/qubes/ cp qubes_rpc/qfile-dom0-unpacker $RPM_BUILD_ROOT/usr/lib/qubes/ cp qubes_rpc/qubes-notify-updates $RPM_BUILD_ROOT/usr/lib/qubes/ cp qubes_rpc/qubes-receive-appmenus $RPM_BUILD_ROOT/usr/lib/qubes/ @@ -176,7 +171,6 @@ cp qubes_rpc/qubes.OpenInVM.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.Op cp qubes_rpc/qubes.VMShell.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.VMShell cp qubes_rpc/qubes.SyncAppMenus.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.SyncAppMenus cp qubes_rpc/qubes.SyncAppMenus $RPM_BUILD_ROOT/etc/qubes_rpc/ -cp ../qrexec/qubes_rpc_multiplexer $RPM_BUILD_ROOT/usr/lib/qubes cp qubes_rpc/qubes.NotifyUpdates.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.NotifyUpdates cp qubes_rpc/qubes.NotifyUpdates $RPM_BUILD_ROOT/etc/qubes_rpc/ cp qubes_rpc/qubes.ReceiveUpdates.policy $RPM_BUILD_ROOT/etc/qubes_rpc/policy/qubes.ReceiveUpdates @@ -489,10 +483,6 @@ fi /etc/xen/scripts/block-snapshot /etc/xen/scripts/block-origin /etc/xen/scripts/vif-route-qubes -/usr/lib/qubes/qrexec_client -/usr/lib/qubes/qubes_rpc_multiplexer -/usr/lib/qubes/qrexec_policy -%dir /etc/qubes_rpc/policy %attr(0664,root,qubes) %config(noreplace) /etc/qubes_rpc/policy/qubes.Filecopy %attr(0664,root,qubes) %config(noreplace) /etc/qubes_rpc/policy/qubes.OpenInVM %attr(0664,root,qubes) %config(noreplace) /etc/qubes_rpc/policy/qubes.SyncAppMenus @@ -502,7 +492,6 @@ fi /etc/qubes_rpc/qubes.SyncAppMenus /etc/qubes_rpc/qubes.NotifyUpdates /etc/qubes_rpc/qubes.ReceiveUpdates -%attr(4750,root,qubes) /usr/lib/qubes/qrexec_daemon %attr(2770,root,qubes) %dir /var/log/qubes %attr(0770,root,qubes) %dir /var/run/qubes /etc/yum.real.repos.d/qubes-cached.repo