diff --git a/Makefile b/Makefile index 4ee4c43..d002a1c 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,6 @@ rpms-dom0: clean: make -C misc clean - make -C qrexec clean make -C qubes-rpc clean make -C doc clean rm -rf qubesagent/*.pyc qubesagent/__pycache__ @@ -56,7 +55,6 @@ clean: all: make -C misc - make -C qrexec make -C qubes-rpc # Dropin Directory @@ -154,7 +152,6 @@ install-sysvinit: install-init install vm-init.d/qubes-core $(DESTDIR)/etc/init.d/ install vm-init.d/qubes-core-netvm $(DESTDIR)/etc/init.d/ install vm-init.d/qubes-firewall $(DESTDIR)/etc/init.d/ - install vm-init.d/qubes-qrexec-agent $(DESTDIR)/etc/init.d/ install vm-init.d/qubes-updates-proxy $(DESTDIR)/etc/init.d/ install vm-init.d/qubes-updates-proxy-forwarder $(DESTDIR)/etc/init.d/ install -D vm-init.d/qubes-core.modules $(DESTDIR)/etc/sysconfig/modules/qubes-core.modules @@ -284,7 +281,6 @@ install-common: install-doc install -m 0755 qubes-rpc/qubes.GetDate $(DESTDIR)/etc/qubes-rpc install -d $(DESTDIR)/etc/qubes/rpc-config - install -m 0644 qubes-rpc/rpc-config.README $(DESTDIR)/etc/qubes/rpc-config/README for config in qubes-rpc/*.config; do \ install -m 0644 $$config $(DESTDIR)/etc/qubes/rpc-config/`basename $$config .config`; \ done diff --git a/debian/control b/debian/control index 1c25964..e8703c8 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,6 @@ Priority: extra Maintainer: unman Build-Depends: libpam0g-dev, - libqrexec-utils-dev, libqubes-rpc-filecopy-dev (>= 3.1.3), libvchan-xen-dev, python, @@ -47,7 +46,7 @@ Depends: python-xdg, python-dbus, qubes-utils (>= 3.1.3), - qubes-core-agent-qrexec, + qubes-core-qrexec, qubesdb-vm, systemd, xdg-user-dirs, @@ -78,25 +77,13 @@ Recommends: Conflicts: qubes-core-agent-linux, firewalld, qubes-core-vm-sysvinit Description: Qubes core agent This package includes various daemons necessary for qubes domU support, - such as qrexec. - -Package: qubes-core-agent-qrexec -Architecture: any -Depends: - libvchan-xen, - ${shlibs:Depends}, - ${misc:Depends} -Replaces: qubes-core-agent (<< 4.0.0-1) -Breaks: qubes-core-agent (<< 4.0.0-1) -Description: Qubes qrexec agent - Agent part of Qubes RPC system. A daemon responsible for starting processes as - requested by dom0 or other VMs, according to dom0-enforced policy. + such as qrexec services. Package: qubes-core-agent-nautilus Architecture: any Depends: python-nautilus, - qubes-core-agent-qrexec, + qubes-core-qrexec, Replaces: qubes-core-agent (<< 4.0.0-1) Breaks: qubes-core-agent (<< 4.0.0-1) Description: Qubes integration for Nautilus @@ -106,7 +93,7 @@ Package: qubes-core-agent-thunar Architecture: any Depends: thunar, - qubes-core-agent-qrexec, + qubes-core-qrexec, Replaces: qubes-core-agent (<< 4.0.0-1) Breaks: qubes-core-agent (<< 4.0.0-1) Description: Qubes integration for Thunar @@ -118,7 +105,7 @@ Depends: fakeroot, yum, yum-utils, - qubes-core-agent-qrexec, + qubes-core-qrexec, Replaces: qubes-core-agent (<< 4.0.0-1) Breaks: qubes-core-agent (<< 4.0.0-1) Description: Scripts required to handle dom0 updates. diff --git a/debian/qubes-core-agent-qrexec.install b/debian/qubes-core-agent-qrexec.install deleted file mode 100644 index f83af2e..0000000 --- a/debian/qubes-core-agent-qrexec.install +++ /dev/null @@ -1,10 +0,0 @@ -etc/pam.d/qrexec -etc/qubes/rpc-config/README -lib/systemd/system/qubes-qrexec-agent.service -usr/bin/qrexec-client-vm -usr/bin/qrexec-fork-server -usr/lib/qubes/qrexec-agent -usr/lib/qubes/qrexec-client-vm -usr/lib/qubes/qrexec_client_vm -usr/lib/qubes/qubes-rpc-multiplexer -usr/share/man/man1/qrexec-client-vm.1.gz diff --git a/debian/qubes-core-agent.postrm b/debian/qubes-core-agent.postrm index c691a52..b116941 100755 --- a/debian/qubes-core-agent.postrm +++ b/debian/qubes-core-agent.postrm @@ -43,7 +43,7 @@ if [ "${1}" = "remove" ] ; then rm /lib/firmware/updates fi - for srv in qubes-sysinit qubes-misc-post qubes-network qubes-qrexec-agent; do + for srv in qubes-sysinit qubes-misc-post qubes-network; do systemctl disable ${srv}.service done fi diff --git a/debian/rules b/debian/rules index 565d881..68d4c83 100755 --- a/debian/rules +++ b/debian/rules @@ -16,7 +16,6 @@ override_dh_auto_build: override_dh_auto_install: make install-deb - make -C qrexec install override_dh_fixperms: dh_fixperms -a -Xqfile-unpacker diff --git a/doc/vm-tools/qrexec-client-vm.rst b/doc/vm-tools/qrexec-client-vm.rst deleted file mode 100644 index 342f517..0000000 --- a/doc/vm-tools/qrexec-client-vm.rst +++ /dev/null @@ -1,85 +0,0 @@ -================ -qrexec-client-vm -================ - -NAME -==== -qrexec-client-vm - call Qubes RPC service - -SYNOPSIS -======== -| qrexec-client-vm [--buffer-size=*BUFFER_SIZE*] *target_vmname* *service* [*local_program* [*local program arguments*]] - -DESCRIPTION -=========== - -Call Qubes RPC (aka qrexec) service to a different VM. The service call request -is sent to dom0, where Qubes RPC policy is evaluated and when it allows the -call, it is forwarded to appropriate target VM (which may be different than -requested, if policy says so). Local program (if given) is started only -when service call is allowed by the policy. - -Remote service can communicate with the caller (``qrexec-client-vm``) using -stdin/stdout. When *local_program* is given, its stdin/stdout is connected to -service stdin/stdout (stderr is not redirected), otherwise - service -stdin/stdout is connected to those of ``qrexec-client-vm``. - -OPTIONS -======= - ---buffer-size=*BUFFER_SIZE* - - Optional buffer size for vchan connection. This size is used as minimum - size for a buffer in each connection direction (read and write). - Default: 64KiB. - -*target_vmname* - - Name of target VM to which service is requested. Qubes RPC policy may - ignore this value and redirect call somewhere else. - - This argument, can contain VM name, or one of special values: - - * ``$default`` or empty string - let Qubes RPC policy decide, without giving any preference - - * ``$dispvm`` - new Disposable VM - - * ``$dispvm:dispvm-template`` - new Disposable VM based on *dispvm-template* - - This field is limited to 31 characters (alphanumeric, plus ``-_.$``). - -*service* - - Requested service. Besides service name, it can contain a service argument - after ``+`` character. For example ``some.service+argument``. - - This field is limited to 63 characters (alphanumeric, plus ``-_.$+``). - -*local_program* - - Full path to local program to be connected with remote service. Optional. - -*local program arguments* - - Arguments to *local_program*. Optional. - -EXIT STATUS -=========== - -If service call is allowed by dom0 and ``qrexec-client-vm`` is started without -*local_program* argument, it reports remote service exit code. - -If service call is allowed by dom0 and ``qrexec-client-vm`` is started with -*local_program* argument, it reports the local program exit code. There is no -way to learn exit code of remote service in this case. - -In both cases, if process (local or remote) was terminated by a signal, exit -status is 128+signal number. - -If service call is denied by dom0, ``qrexec-client-vm`` exit with status 126. - -AUTHORS -======= -| Joanna Rutkowska -| Rafal Wojtczuk -| Marek Marczykowski-Górecki diff --git a/qrexec/Makefile b/qrexec/Makefile deleted file mode 100644 index 4200b8f..0000000 --- a/qrexec/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -CC ?= gcc -CFLAGS += -I. -g -O2 -Wall -Wextra -Werror -pie -fPIC `pkg-config --cflags vchan-$(BACKEND_VMM)` -LDFLAGS += -pie -LDLIBS = `pkg-config --libs vchan-$(BACKEND_VMM)` -lqrexec-utils -lpam - -all: qrexec-agent qrexec-client-vm qrexec-fork-server -qrexec-agent: qrexec-agent.o qrexec-agent-data.o -qrexec-fork-server: qrexec-fork-server.o qrexec-agent-data.o -qrexec-client-vm: qrexec-client-vm.o qrexec-agent-data.o -clean: - rm -f *.o *~ qrexec-agent qrexec-client-vm - -install: - install -d $(DESTDIR)/etc/qubes-rpc - install -d $(DESTDIR)/usr/lib/qubes $(DESTDIR)/usr/bin - install qrexec-agent $(DESTDIR)/usr/lib/qubes - install qrexec-client-vm $(DESTDIR)/usr/bin - ln -s ../../bin/qrexec-client-vm $(DESTDIR)/usr/lib/qubes/qrexec-client-vm - ln -s ../../bin/qrexec-client-vm $(DESTDIR)/usr/lib/qubes/qrexec_client_vm - install qrexec-fork-server $(DESTDIR)/usr/bin - install qubes-rpc-multiplexer $(DESTDIR)/usr/lib/qubes -ifeq ($(shell lsb_release -is), Debian) - install -D -m 0644 qrexec.pam.debian $(DESTDIR)/etc/pam.d/qrexec -else ifeq ($(shell lsb_release -is), Ubuntu) - install -D -m 0644 qrexec.pam.debian $(DESTDIR)/etc/pam.d/qrexec -else ifeq ($(shell lsb_release -is), Arch) - install -D -m 0644 qrexec.pam.archlinux $(DESTDIR)/etc/pam.d/qrexec -else - install -D -m 0644 qrexec.pam $(DESTDIR)/etc/pam.d/qrexec -endif - diff --git a/qrexec/qrexec-agent-data.c b/qrexec/qrexec-agent-data.c deleted file mode 100644 index 0803611..0000000 --- a/qrexec/qrexec-agent-data.c +++ /dev/null @@ -1,594 +0,0 @@ -/* - * The Qubes OS Project, http://www.qubes-os.org - * - * Copyright (C) 2013 Marek Marczykowski-Górecki - * - * 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 -#include -#include "qrexec.h" -#include "libqrexec-utils.h" -#include "qrexec-agent.h" - -#define VCHAN_BUFFER_SIZE 65536 - -static volatile int child_exited; -static volatile int stdio_socket_requested; -int stdout_msg_type = MSG_DATA_STDOUT; -pid_t child_process_pid; -int remote_process_status = 0; - -static void sigchld_handler(int __attribute__((__unused__))x) -{ - child_exited = 1; -} - -static void sigusr1_handler(int __attribute__((__unused__))x) -{ - stdio_socket_requested = 1; - signal(SIGUSR1, SIG_IGN); -} - -void prepare_child_env() { - char pid_s[10]; - - signal(SIGCHLD, sigchld_handler); - signal(SIGUSR1, sigusr1_handler); - snprintf(pid_s, sizeof(pid_s), "%d", getpid()); - setenv("QREXEC_AGENT_PID", pid_s, 1); -} - -int handle_handshake(libvchan_t *ctrl) -{ - struct msg_header hdr; - struct peer_info info; - int actual_version; - - /* send own HELLO */ - 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; - } - - /* receive MSG_HELLO from remote */ - 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); - return -1; - } - - return actual_version; -} - - -int handle_just_exec(char *cmdline) -{ - int fdn, pid; - - switch (pid = fork()) { - case -1: - perror("fork"); - return -1; - case 0: - fdn = open("/dev/null", O_RDWR); - fix_fds(fdn, fdn, fdn); - do_exec(cmdline); - perror("execl"); - exit(1); - default:; - } - fprintf(stderr, "executed (nowait) %s pid %d\n", cmdline, pid); - return 0; -} - -void send_exit_code(libvchan_t *data_vchan, int status) -{ - struct msg_header hdr; - hdr.type = MSG_DATA_EXIT_CODE; - hdr.len = sizeof(status); - if (libvchan_send(data_vchan, &hdr, sizeof(hdr)) < 0) - handle_vchan_error("write hdr"); - if (libvchan_send(data_vchan, &status, sizeof(status)) < 0) - handle_vchan_error("write status"); - fprintf(stderr, "send exit code %d\n", status); -} - -/* handle data from specified FD and send over vchan link - * Return: - * -1 - vchan error occurred - * 0 - EOF received, do not attempt to access this FD again - * 1 - some data processed, call it again when buffer space and more data - * available - */ -int handle_input(libvchan_t *vchan, int fd, int msg_type) -{ - char buf[MAX_DATA_CHUNK]; - int len; - struct msg_header hdr; - - hdr.type = msg_type; - while (libvchan_buffer_space(vchan) > (int)sizeof(struct msg_header)) { - len = libvchan_buffer_space(vchan)-sizeof(struct msg_header); - if (len > (int)sizeof(buf)) - len = sizeof(buf); - len = read(fd, buf, len); - if (len < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) - return 1; - else - return -1; - } - hdr.len = len; - if (libvchan_send(vchan, &hdr, sizeof(hdr)) < 0) - return -1; - - if (len && !write_vchan_all(vchan, buf, len)) - return -1; - - if (len == 0) { - /* restore flags */ - set_block(fd); - if (shutdown(fd, SHUT_RD) < 0) { - if (errno == ENOTSOCK) - close(fd); - } - return 0; - } - } - return 1; -} - -/* handle data from vchan and send it to specified FD - * Return: - * -2 - remote process terminated, do not send more data to it - * in this case "status" will be set - * -1 - vchan error occurred - * 0 - EOF received, do not attempt to access this FD again - * 1 - maybe some data processed, call it again when buffer space and more data - * available - */ - -int handle_remote_data(libvchan_t *data_vchan, int stdin_fd, int *status, - struct buffer *stdin_buf) -{ - struct msg_header hdr; - char buf[MAX_DATA_CHUNK]; - - /* do not receive any data if we have something already buffered */ - switch (flush_client_data(stdin_fd, stdin_buf)) { - case WRITE_STDIN_OK: - break; - case WRITE_STDIN_BUFFERED: - return 1; - case WRITE_STDIN_ERROR: - perror("write"); - return 0; - } - - while (libvchan_data_ready(data_vchan) > 0) { - if (libvchan_recv(data_vchan, &hdr, sizeof(hdr)) < 0) - return -1; - if (hdr.len > MAX_DATA_CHUNK) { - fprintf(stderr, "Too big data chunk received: %d > %d\n", - hdr.len, MAX_DATA_CHUNK); - return -1; - } - if (!read_vchan_all(data_vchan, buf, hdr.len)) - return -1; - - switch (hdr.type) { - /* handle both directions because this can be either server or client - * of VM-VM connection */ - case MSG_DATA_STDIN: - case MSG_DATA_STDOUT: - if (stdin_fd < 0) - /* discard the data */ - continue; - if (hdr.len == 0) { - /* restore flags */ - set_block(stdin_fd); - if (!child_process_pid || stdin_fd == 1 || - (shutdown(stdin_fd, SHUT_WR) == -1 && - errno == ENOTSOCK)) { - close(stdin_fd); - } - stdin_fd = -1; - return 0; - } else { - switch (write_stdin(stdin_fd, buf, hdr.len, stdin_buf)) { - case WRITE_STDIN_OK: - break; - case WRITE_STDIN_BUFFERED: - return 1; - case WRITE_STDIN_ERROR: - if (errno == EPIPE || errno == ECONNRESET) { - if (!child_process_pid || stdin_fd == 1 || - (shutdown(stdin_fd, SHUT_WR) == -1 && - errno == ENOTSOCK)) { - close(stdin_fd); - } - stdin_fd = -1; - } else { - perror("write"); - } - return 0; - } - } - break; - case MSG_DATA_STDERR: - /* stderr of remote service, log locally */ - if (!write_all(2, buf, hdr.len)) { - perror("write"); - /* only log the error */ - } - break; - case MSG_DATA_EXIT_CODE: - /* remote process exited, so there is no sense to send any data - * to it */ - if (hdr.len < sizeof(*status)) - *status = 255; - else - memcpy(status, buf, sizeof(*status)); - return -2; - } - } - return 1; -} - -int process_child_io(libvchan_t *data_vchan, - int stdin_fd, int stdout_fd, int stderr_fd) -{ - fd_set rdset, wrset; - int vchan_fd; - sigset_t selectmask; - int child_process_status = -1; - int remote_process_status = -1; - int ret, max_fd; - struct timespec zero_timeout = { 0, 0 }; - struct timespec normal_timeout = { 10, 0 }; - struct buffer stdin_buf; - - sigemptyset(&selectmask); - sigaddset(&selectmask, SIGCHLD); - sigprocmask(SIG_BLOCK, &selectmask, NULL); - sigemptyset(&selectmask); - - set_nonblock(stdin_fd); - set_nonblock(stdout_fd); - set_nonblock(stderr_fd); - - buffer_init(&stdin_buf); - while (1) { - if (child_exited) { - int status; - if (child_process_pid && - waitpid(child_process_pid, &status, WNOHANG) > 0) { - if (WIFSIGNALED(status)) - child_process_status = 128 + WTERMSIG(status); - else - child_process_status = WEXITSTATUS(status); - if (stdin_fd >= 0) { - /* restore flags */ - set_block(stdin_fd); - if (!child_process_pid || stdin_fd == 1 || - (shutdown(stdin_fd, SHUT_WR) == -1 && - errno == ENOTSOCK)) { - close(stdin_fd); - } - stdin_fd = -1; - } - } - child_exited = 0; - } - - /* if all done, exit the loop */ - if ((!child_process_pid || child_process_status > -1) && - (child_process_pid || remote_process_status > -1) && - stdin_fd == -1 && stdout_fd == -1 && stderr_fd == -1) { - if (child_process_status > -1) { - send_exit_code(data_vchan, child_process_status); - } - break; - } - /* also if vchan is disconnected (and we processed all the data), there - * is no sense of processing further data */ - if (!libvchan_data_ready(data_vchan) && - !libvchan_is_open(data_vchan) && - !buffer_len(&stdin_buf)) { - break; - } - /* child signaled desire to use single socket for both stdin and stdout */ - if (stdio_socket_requested) { - if (stdout_fd != -1 && stdout_fd != stdin_fd) - close(stdout_fd); - stdout_fd = stdin_fd; - stdio_socket_requested = 0; - } - /* otherwise handle the events */ - - FD_ZERO(&rdset); - FD_ZERO(&wrset); - max_fd = -1; - vchan_fd = libvchan_fd_for_select(data_vchan); - if (libvchan_buffer_space(data_vchan) > (int)sizeof(struct msg_header)) { - if (stdout_fd >= 0) { - FD_SET(stdout_fd, &rdset); - if (stdout_fd > max_fd) - max_fd = stdout_fd; - } - if (stderr_fd >= 0) { - FD_SET(stderr_fd, &rdset); - if (stderr_fd > max_fd) - max_fd = stderr_fd; - } - } - FD_SET(vchan_fd, &rdset); - if (vchan_fd > max_fd) - max_fd = vchan_fd; - /* if we have something buffered for the child process, wake also on - * writable stdin */ - if (stdin_fd > -1 && buffer_len(&stdin_buf)) { - FD_SET(stdin_fd, &wrset); - if (stdin_fd > max_fd) - max_fd = stdin_fd; - } - - if (!buffer_len(&stdin_buf) && libvchan_data_ready(data_vchan) > 0) { - /* check for other FDs, but exit immediately */ - ret = pselect(max_fd + 1, &rdset, &wrset, NULL, &zero_timeout, &selectmask); - } else - ret = pselect(max_fd + 1, &rdset, &wrset, NULL, &normal_timeout, &selectmask); - if (ret < 0) { - if (errno == EINTR) - continue; - else { - perror("pselect"); - /* TODO */ - break; - } - } - - /* clear event pending flag */ - if (FD_ISSET(vchan_fd, &rdset)) { - if (libvchan_wait(data_vchan) < 0) - handle_vchan_error("wait"); - } - - /* handle_remote_data will check if any data is available */ - switch (handle_remote_data(data_vchan, stdin_fd, &remote_process_status, &stdin_buf)) { - case -1: - handle_vchan_error("read"); - break; - case 0: - stdin_fd = -1; - break; - case -2: - /* remote process exited, no sense in sending more data to it; - * be careful to not shutdown socket inherited from parent */ - if (!child_process_pid || stdout_fd == 0 || - (shutdown(stdout_fd, SHUT_RD) == -1 && - errno == ENOTSOCK)) { - close(stdout_fd); - } - stdout_fd = -1; - close(stderr_fd); - stderr_fd = -1; - /* if we do not care for any local process, return remote process code */ - if (child_process_pid == 0) - return remote_process_status; - break; - } - if (stdout_fd >= 0 && FD_ISSET(stdout_fd, &rdset)) { - switch (handle_input(data_vchan, stdout_fd, stdout_msg_type)) { - case -1: - handle_vchan_error("send"); - break; - case 0: - stdout_fd = -1; - break; - } - } - if (stderr_fd >= 0 && FD_ISSET(stderr_fd, &rdset)) { - switch (handle_input(data_vchan, stderr_fd, MSG_DATA_STDERR)) { - case -1: - handle_vchan_error("send"); - break; - case 0: - stderr_fd = -1; - break; - } - } - } - /* make sure that all the pipes/sockets are closed, so the child process - * (if any) will know that the connection is terminated */ - if (stdout_fd != -1) { - /* restore flags */ - set_block(stdout_fd); - /* be careful to not shutdown socket inherited from parent */ - if (!child_process_pid || stdout_fd == 0 || - (shutdown(stdout_fd, SHUT_RD) == -1 && errno == ENOTSOCK)) { - close(stdout_fd); - } - stdout_fd = -1; - } - if (stdin_fd != -1) { - /* restore flags */ - set_block(stdin_fd); - /* be careful to not shutdown socket inherited from parent */ - if (!child_process_pid || stdin_fd == 1 || - (shutdown(stdin_fd, SHUT_WR) == -1 && errno == ENOTSOCK)) { - close(stdin_fd); - } - stdin_fd = -1; - } - if (stderr_fd != -1) { - /* restore flags */ - set_block(stderr_fd); - close(stderr_fd); - stderr_fd = -1; - } - if (child_process_pid == 0) - return remote_process_status; - return child_process_status; -} - -/* Behaviour depends on type parameter: - * MSG_SERVICE_CONNECT - create vchan server, pass the data to/from given FDs - * (stdin_fd, stdout_fd, stderr_fd), then return remote process exit code - * MSG_JUST_EXEC - connect to vchan server, fork+exec process given by cmdline - * parameter, send artificial exit code "0" (local process can still be - * running), then return 0 - * MSG_EXEC_CMDLINE - connect to vchan server, fork+exec process given by - * cmdline parameter, pass the data to/from that process, then return local - * process exit code - * - * buffer_size is about vchan buffer allocated (only for vchan server cases), - * use 0 to use built-in default (64k); needs to be power of 2 - */ -int handle_new_process_common(int type, int connect_domain, int connect_port, - char *cmdline, int cmdline_len, /* MSG_JUST_EXEC and MSG_EXEC_CMDLINE */ - int stdin_fd, int stdout_fd, int stderr_fd /* MSG_SERVICE_CONNECT */, - int buffer_size) -{ - libvchan_t *data_vchan; - int exit_code = 0; - pid_t pid; - - if (type != MSG_SERVICE_CONNECT) { - assert(cmdline != NULL); - cmdline[cmdline_len-1] = 0; - } - - if (buffer_size == 0) - buffer_size = VCHAN_BUFFER_SIZE; - - if (type == MSG_SERVICE_CONNECT) { - data_vchan = libvchan_server_init(connect_domain, connect_port, - buffer_size, buffer_size); - if (data_vchan) - libvchan_wait(data_vchan); - } else { - data_vchan = libvchan_client_init(connect_domain, connect_port); - } - if (!data_vchan) { - fprintf(stderr, "Data vchan connection failed\n"); - exit(1); - } - handle_handshake(data_vchan); - - prepare_child_env(); - /* TODO: use setresuid to allow child process to actually send the signal? */ - - switch (type) { - case MSG_JUST_EXEC: - send_exit_code(data_vchan, handle_just_exec(cmdline)); - break; - case MSG_EXEC_CMDLINE: - do_fork_exec(cmdline, &pid, &stdin_fd, &stdout_fd, &stderr_fd); - fprintf(stderr, "executed %s pid %d\n", cmdline, pid); - child_process_pid = pid; - exit_code = process_child_io(data_vchan, stdin_fd, stdout_fd, stderr_fd); - fprintf(stderr, "pid %d exited with %d\n", pid, exit_code); - break; - case MSG_SERVICE_CONNECT: - child_process_pid = 0; - stdout_msg_type = MSG_DATA_STDIN; - exit_code = process_child_io(data_vchan, stdin_fd, stdout_fd, stderr_fd); - break; - } - libvchan_close(data_vchan); - return exit_code; -} - -/* Returns PID of data processing process */ -pid_t handle_new_process(int type, int connect_domain, int connect_port, - char *cmdline, int cmdline_len) -{ - int exit_code; - pid_t pid; - assert(type != MSG_SERVICE_CONNECT); - - switch (pid=fork()){ - case -1: - perror("fork"); - return -1; - case 0: - break; - default: - return pid; - } - - /* child process */ - exit_code = handle_new_process_common(type, connect_domain, connect_port, - cmdline, cmdline_len, - -1, -1, -1, 0); - - exit(exit_code); - /* suppress warning */ - return 0; -} - -/* Returns exit code of remote process */ -int handle_data_client(int type, int connect_domain, int connect_port, - int stdin_fd, int stdout_fd, int stderr_fd, int buffer_size) -{ - int exit_code; - - assert(type == MSG_SERVICE_CONNECT); - - exit_code = handle_new_process_common(type, connect_domain, connect_port, - NULL, 0, stdin_fd, stdout_fd, stderr_fd, buffer_size); - return exit_code; -} diff --git a/qrexec/qrexec-agent.c b/qrexec/qrexec-agent.c deleted file mode 100644 index e368eb2..0000000 --- a/qrexec/qrexec-agent.c +++ /dev/null @@ -1,948 +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 -#define HAVE_PAM - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_PAM -#include -#endif -#include "qrexec.h" -#include -#include "libqrexec-utils.h" -#include "qrexec-agent.h" - -struct _connection_info { - int pid; /* pid of child process handling the data */ - int fd; /* socket to process handling the data (wait for EOF here) */ - int connect_domain; - int connect_port; -}; - -/* structure describing a single request waiting for qubes.WaitForSession to - * finish */ -struct _waiting_request { - int type; - int connect_domain; - int connect_port; - char *cmdline; -}; - -int max_process_fd = -1; - -/* */ -struct _connection_info connection_info[MAX_FDS]; - -struct _waiting_request requests_waiting_for_session[MAX_FDS]; - -libvchan_t *ctrl_vchan; - -pid_t wait_for_session_pid = -1; - -int trigger_fd; - -int meminfo_write_started = 0; - -void handle_server_exec_request_do(int type, int connect_domain, int connect_port, char *cmdline); - -void no_colon_in_cmd() -{ - fprintf(stderr, - "cmdline is supposed to be in user:command form\n"); - exit(1); -} - -#ifdef HAVE_PAM -int pam_conv_callback(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr __attribute__((__unused__))) -{ - int i; - struct pam_response *resp_array = - calloc(sizeof(struct pam_response), num_msg); - - if (resp_array == NULL) - return PAM_BUF_ERR; - - for (i=0; imsg_style == PAM_ERROR_MSG) - fprintf(stderr, "%s", msg[i]->msg); - if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF || - msg[i]->msg_style == PAM_PROMPT_ECHO_ON) { - resp_array[i].resp = strdup(""); - resp_array[i].resp_retcode = 0; - } - } - *resp = resp_array; - return PAM_SUCCESS; -} - -static struct pam_conv conv = { - pam_conv_callback, - NULL -}; -#endif - -/* Start program requested by dom0 in already prepared process - * (stdin/stdout/stderr already set, etc) - * Called in two cases: - * MSG_JUST_EXEC - from qrexec-agent-data.c:handle_new_process_common->handle_just_exec - * MSG_EXEC_CMDLINE - from - * qrexec-agent-data.c:handle_new_process_common->do_fork_exec (callback - * registerd with register_exec_func in init() here) - * - * cmd parameter came from dom0 (MSG_JUST_EXEC or MSG_EXEC_CMDLINE messages), so - * is trusted. Even in VM-VM service request, the command here is controlled by - * dom0 - it will be in form: - * RPC_REQUEST_COMMAND " " service_name " " source_vm_name - * where service_name is already validated against Qrexec RPC policy - * - * If dom0 sends overly long cmd, it will probably crash qrexec-agent (unless - * process can allocate up to 4GB on both stack and heap), sorry. - */ -void do_exec(char *cmd) -{ - char *realcmd = index(cmd, ':'), *user; -#ifdef HAVE_PAM - int retval, status; - pam_handle_t *pamh=NULL; - struct passwd *pw; - struct passwd pw_copy; - pid_t child, pid; - char **env; - char env_buf[64]; - char *arg0; - char *shell_basename; -#endif - - if (!realcmd) - no_colon_in_cmd(); - /* mark end of username and move to command */ - user=strndup(cmd,realcmd-cmd); - realcmd++; - /* ignore "nogui:" prefix in linux agent */ - if (strncmp(realcmd, NOGUI_CMD_PREFIX, NOGUI_CMD_PREFIX_LEN) == 0) - realcmd += NOGUI_CMD_PREFIX_LEN; - - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); - -#ifdef HAVE_PAM - pw = getpwnam (user); - if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0] - && pw->pw_passwd)) { - fprintf(stderr, "user %s does not exist", user); - exit(1); - } - - /* Make a copy of the password information and point pw at the local - * copy instead. Otherwise, some systems (e.g. Linux) would clobber - * the static data through the getlogin call. - */ - pw_copy = *pw; - pw = &pw_copy; - pw->pw_name = strdup(pw->pw_name); - pw->pw_passwd = strdup(pw->pw_passwd); - pw->pw_dir = strdup(pw->pw_dir); - pw->pw_shell = strdup(pw->pw_shell); - endpwent(); - - shell_basename = basename (pw->pw_shell); - /* this process is going to die shortly, so don't care about freeing */ - arg0 = malloc (strlen (shell_basename) + 2); - if (!arg0) - goto error; - arg0[0] = '-'; - strcpy (arg0 + 1, shell_basename); - - retval = pam_start("qrexec", user, &conv, &pamh); - if (retval != PAM_SUCCESS) - goto error; - - retval = pam_authenticate(pamh, 0); - if (retval != PAM_SUCCESS) - goto error; - - retval = initgroups(pw->pw_name, pw->pw_gid); - if (retval == -1) { - perror("initgroups"); - goto error; - } - - retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); - if (retval != PAM_SUCCESS) - goto error; - - retval = pam_open_session(pamh, 0); - if (retval != PAM_SUCCESS) - goto error; - - /* provide this variable to child process */ - snprintf(env_buf, sizeof(env_buf), "QREXEC_AGENT_PID=%d", getppid()); - retval = pam_putenv(pamh, env_buf); - if (retval != PAM_SUCCESS) - goto error; - snprintf(env_buf, sizeof(env_buf), "HOME=%s", pw->pw_dir); - retval = pam_putenv(pamh, env_buf); - if (retval != PAM_SUCCESS) - goto error; - snprintf(env_buf, sizeof(env_buf), "SHELL=%s", pw->pw_shell); - retval = pam_putenv(pamh, env_buf); - if (retval != PAM_SUCCESS) - goto error; - snprintf(env_buf, sizeof(env_buf), "USER=%s", pw->pw_name); - retval = pam_putenv(pamh, env_buf); - if (retval != PAM_SUCCESS) - goto error; - snprintf(env_buf, sizeof(env_buf), "LOGNAME=%s", pw->pw_name); - retval = pam_putenv(pamh, env_buf); - if (retval != PAM_SUCCESS) - goto error; - - /* FORK HERE */ - child = fork (); - - switch (child) { - case -1: - goto error; - case 0: - /* child */ - - if (setgid (pw->pw_gid)) - exit(126); - if (setuid (pw->pw_uid)) - exit(126); - setsid(); - /* This is a copy but don't care to free as we exec later anyway. */ - env = pam_getenvlist (pamh); - - /* try to enter home dir, but don't abort if it fails */ - retval = chdir(pw->pw_dir); - if (retval == -1) - warn("chdir(%s)", pw->pw_dir); - - /* call QUBESRPC if requested */ - exec_qubes_rpc_if_requested(realcmd, env); - - /* otherwise exec shell */ - execle(pw->pw_shell, arg0, "-c", realcmd, (char*)NULL, env); - exit(127); - default: - /* parent */ - /* close std*, so when child process closes them, qrexec-agent will receive EOF */ - /* this is the main purpose of this reimplementation of /bin/su... */ - close(0); - close(1); - close(2); - } - - /* reachable only in parent */ - pid = waitpid (child, &status, 0); - if (pid != (pid_t)-1) { - if (WIFSIGNALED (status)) - status = WTERMSIG (status) + 128; - else - status = WEXITSTATUS (status); - } else - status = 1; - - retval = pam_close_session (pamh, 0); - - retval = pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT); - - if (pam_end(pamh, retval) != PAM_SUCCESS) { /* close Linux-PAM */ - pamh = NULL; - exit(1); - } - exit(status); -error: - pam_end(pamh, PAM_ABORT); - exit(1); -#else - /* call QUBESRPC if requested */ - exec_qubes_rpc_if_requested(realcmd, environ); - - /* otherwise exec shell */ - execl("/bin/su", "su", "-", user, "-c", realcmd, NULL); - perror("execl"); - exit(1); -#endif - -} - -void handle_vchan_error(const char *op) -{ - fprintf(stderr, "Error while vchan %s, exiting\n", op); - exit(1); -} - -int my_sd_notify(int unset_environment, const char *state) { - struct sockaddr_un addr; - int fd; - int ret = -1; - - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, getenv("NOTIFY_SOCKET"), sizeof(addr.sun_path)-1); - addr.sun_path[sizeof(addr.sun_path)-1] = '\0'; - if (addr.sun_path[0] == '@') - addr.sun_path[0] = '\0'; - - if (unset_environment) - unsetenv("NOTIFY_SOCKET"); - - fd = socket(AF_UNIX, SOCK_DGRAM, 0); - if (fd == -1) { - perror("sd_notify socket"); - return -1; - } - - if (connect(fd, &addr, sizeof(addr)) == -1) { - perror("sd_notify connect"); - goto out; - } - - if (send(fd, state, strlen(state), 0) == -1) { - perror("sd_notify send"); - goto out; - } - - ret = 0; -out: - close(fd); - return ret; -} - -void init() -{ - mode_t old_umask; - /* FIXME: This 0 is remote domain ID */ - ctrl_vchan = libvchan_server_init(0, VCHAN_BASE_PORT, 4096, 4096); - if (!ctrl_vchan) - handle_vchan_error("server_init"); - if (handle_handshake(ctrl_vchan) < 0) - exit(1); - old_umask = umask(0); - trigger_fd = get_server_socket(QREXEC_AGENT_TRIGGER_PATH); - umask(old_umask); - register_exec_func(do_exec); - - /* wait for qrexec daemon */ - while (!libvchan_is_open(ctrl_vchan)) - libvchan_wait(ctrl_vchan); - - if (getenv("NOTIFY_SOCKET")) { - my_sd_notify(1, "READY=1"); - } -} - -void wake_meminfo_writer() -{ - FILE *f; - int 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) { - fclose(f); - /* no meminfo-writer found, ignoring */ - return; - } - - fclose(f); - if (pid <= 1 || pid > 0xffff) { - /* check within acceptable range */ - return; - } - if (kill(pid, SIGUSR1) < 0) { - /* Can't send signal */ - return; - } - meminfo_write_started = 1; -} - -int try_fork_server(int type, int connect_domain, int connect_port, - char *cmdline, int cmdline_len) { - char username[cmdline_len]; - char *colon; - char *fork_server_socket_path; - int s, len; - struct sockaddr_un remote; - struct qrexec_cmd_info info; - - strncpy(username, cmdline, cmdline_len); - colon = index(username, ':'); - if (!colon) - return -1; - *colon = '\0'; - - if (asprintf(&fork_server_socket_path, QREXEC_FORK_SERVER_SOCKET, username) < 0) { - fprintf(stderr, "Memory allocation failed\n"); - return -1; - } - - remote.sun_family = AF_UNIX; - strncpy(remote.sun_path, fork_server_socket_path, - sizeof(remote.sun_path) - 1); - free(fork_server_socket_path); - - if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - perror("socket"); - return -1; - } - len = strlen(remote.sun_path) + sizeof(remote.sun_family); - if (connect(s, (struct sockaddr *) &remote, len) == -1) { - if (errno != ECONNREFUSED && errno != ENOENT) - perror("connect"); - close(s); - return -1; - } - - info.type = type; - info.connect_domain = connect_domain; - info.connect_port = connect_port; - info.cmdline_len = cmdline_len-(strlen(username)+1); - if (!write_all(s, &info, sizeof(info))) { - perror("write"); - close(s); - return -1; - } - if (!write_all(s, colon+1, info.cmdline_len)) { - perror("write"); - close(s); - return -1; - } - - return s; -} - - -void register_vchan_connection(pid_t pid, int fd, int domain, int port) -{ - int i; - - for (i = 0; i < MAX_FDS; i++) { - if (connection_info[i].pid == 0) { - connection_info[i].pid = pid; - connection_info[i].fd = fd; - connection_info[i].connect_domain = domain; - connection_info[i].connect_port = port; - return; - } - } - - fprintf(stderr, "No free slot for child %d (connection to %d:%d)\n", pid, domain, port); -} - -/* Load service configuration from /etc/qubes/rpc-config/ - * (QUBES_RPC_CONFIG_DIR), currently only wait-for-session option supported. - * - * Return: - * 1 - config successfuly loaded - * 0 - config not found - * -1 - other error - */ -int load_service_config(const char *service_name, int *wait_for_session) { - char filename[256]; - char config[MAX_CONFIG_SIZE]; - char *config_iter = config; - FILE *config_file; - size_t read_count; - char *current_line; - - if (snprintf(filename, sizeof(filename), "%s/%s", - QUBES_RPC_CONFIG_DIR, service_name) >= (int)sizeof(filename)) { - /* buffer too small?! */ - return -1; - } - - config_file = fopen(filename, "r"); - if (!config_file) { - if (errno == ENOENT) - return 0; - else { - fprintf(stderr, "Failed to load %s\n", filename); - return -1; - } - } - - read_count = fread(config, 1, sizeof(config)-1, config_file); - - if (ferror(config_file)) { - fclose(config_file); - return -1; - } - - // config is a text file, should not have \0 inside; but when it has, part - // after it will be ignored - config[read_count] = 0; - - while ((current_line = strsep(&config_iter, "\n"))) { - // ignore comments - if (current_line[0] == '#') - continue; - sscanf(current_line, "wait-for-session=%d", wait_for_session); - } - - fclose(config_file); - return 1; -} - -/* Check if requested command/service require GUI session and if so, initiate - * waiting process. - * - * Return: - * - 1 - waiting is needed, caller should register request to be proceeded - * only after session is started) - * - 0 - waiting is not needed, caller may proceed with request immediately - */ -int wait_for_session_maybe(char *cmdline) { - char *realcmd = index(cmdline, ':'); - char *user, *service_name, *source_domain, *service_argument; - int stdin_pipe[2]; - int wait_for_session = 0; - - if (!realcmd) - /* no colon in command line, this will be properly reported later */ - return 0; - - /* "nogui:" prefix have priority - do not wait for session */ - if (strncmp(realcmd, NOGUI_CMD_PREFIX, NOGUI_CMD_PREFIX_LEN) == 0) - return 0; - - /* extract username */ - user = strndup(cmdline, realcmd - cmdline); - realcmd++; - - /* wait for session only for service requests */ - if (strncmp(realcmd, RPC_REQUEST_COMMAND " ", RPC_REQUEST_COMMAND_LEN+1) != 0) { - free(user); - return 0; - } - - realcmd += RPC_REQUEST_COMMAND_LEN+1; - /* now realcmd contains service name (possibly with argument after '+' - * char) and source domain name, after space */ - source_domain = index(realcmd, ' '); - if (!source_domain) { - /* qrexec-rpc-multiplexer will properly report this */ - free(user); - return 0; - } - service_name = strndup(realcmd, source_domain - realcmd); - source_domain++; - - /* first try to load config for specific argument */ - switch (load_service_config(service_name, &wait_for_session)) { - case 0: - /* no config for specific argument, try for bare service name */ - service_argument = index(service_name, '+'); - if (!service_argument) { - /* there was no argument, so no config at all - do not wait for - * session */ - free(user); - return 0; - } - /* cut off the argument */ - *service_argument = '\0'; - - if (load_service_config(service_name, &wait_for_session) != 1) { - /* no config, or load error -> no wait for session */ - free(user); - return 0; - } - break; - - case 1: - /* config loaded */ - break; - - case -1: - /* load error -> no wait for session */ - free(user); - return 0; - } - - if (!wait_for_session) { - /* setting not set, or set to 0 */ - free(user); - return 0; - } - - /* ok, now we know that service is configured to wait for session */ - - if (wait_for_session_pid != -1) { - /* we're already waiting */ - free(user); - return 1; - } - - if (pipe(stdin_pipe) == -1) { - perror("pipe for wait-for-session"); - free(user); - return 0; - } - /* start waiting process */ - wait_for_session_pid = fork(); - switch (wait_for_session_pid) { - case 0: - close(stdin_pipe[1]); - dup2(stdin_pipe[0], 0); - execl("/etc/qubes-rpc/qubes.WaitForSession", "qubes.WaitForSession", - source_domain, (char*)NULL); - exit(1); - case -1: - perror("fork wait-for-session"); - free(user); - return 0; - default: - close(stdin_pipe[0]); - if (write(stdin_pipe[1], user, strlen(user)) == -1) - perror("write error"); - if (write(stdin_pipe[1], "\n", 1) == -1) - perror("write error"); - close(stdin_pipe[1]); - } - free(user); - /* qubes.WaitForSession started, postpone request until it report back */ - return 1; -} - - -/* hdr parameter is received from dom0, so it is trusted */ -void handle_server_exec_request_init(struct msg_header *hdr) -{ - struct exec_params params; - int buf_len = hdr->len-sizeof(params); - char buf[buf_len]; - - assert(hdr->len >= sizeof(params)); - - if (libvchan_recv(ctrl_vchan, ¶ms, sizeof(params)) < 0) - handle_vchan_error("read exec params"); - if (libvchan_recv(ctrl_vchan, buf, buf_len) < 0) - handle_vchan_error("read exec cmd"); - - buf[buf_len-1] = 0; - - if (hdr->type != MSG_SERVICE_CONNECT && wait_for_session_maybe(buf)) { - /* waiting for session, postpone actual call */ - int slot_index; - for (slot_index = 0; slot_index < MAX_FDS; slot_index++) - if (!requests_waiting_for_session[slot_index].cmdline) - break; - if (slot_index == MAX_FDS) { - /* no free slots */ - fprintf(stderr, "No free slots for waiting for GUI session, continuing!\n"); - } else { - requests_waiting_for_session[slot_index].type = hdr->type; - requests_waiting_for_session[slot_index].connect_domain = params.connect_domain; - requests_waiting_for_session[slot_index].connect_port = params.connect_port; - requests_waiting_for_session[slot_index].cmdline = strdup(buf); - /* nothing to do now, when we get GUI session, we'll continue */ - return; - } - } - - handle_server_exec_request_do(hdr->type, params.connect_domain, params.connect_port, buf); -} - -void handle_server_exec_request_do(int type, int connect_domain, int connect_port, char *cmdline) { - int client_fd; - pid_t child_agent; - int cmdline_len = strlen(cmdline) + 1; // size of cmdline, including \0 at the end - struct exec_params params = { - .connect_domain = connect_domain, - .connect_port = connect_port, - }; - - if ((type == MSG_EXEC_CMDLINE || type == MSG_JUST_EXEC) && - !strstr(cmdline, ":nogui:")) { - int child_socket; - - child_socket = try_fork_server(type, - params.connect_domain, params.connect_port, - cmdline, cmdline_len); - if (child_socket >= 0) { - register_vchan_connection(-1, child_socket, - params.connect_domain, params.connect_port); - return; - } - } - - if (type == MSG_SERVICE_CONNECT && sscanf(cmdline, "SOCKET%d", &client_fd)) { - /* FIXME: Maybe add some check if client_fd is really FD to some - * qrexec-client-vm process; but this data comes from qrexec-daemon - * (which sends back what it got from us earlier), so it isn't critical. - */ - if (write(client_fd, ¶ms, sizeof(params)) < 0) { - /* ignore */ - } - /* No need to send request_id (buf) - the client don't need it, there - * is only meaningless (for the client) socket FD */ - /* Register connection even if there was an error sending params to - * qrexec-client-vm. This way the mainloop will clean the things up - * (close socket, send MSG_CONNECTION_TERMINATED) when qrexec-client-vm - * will close the socket (terminate itself). */ - register_vchan_connection(-1, client_fd, - params.connect_domain, params.connect_port); - return; - } - - /* No fork server case */ - child_agent = handle_new_process(type, - params.connect_domain, params.connect_port, - cmdline, cmdline_len); - - register_vchan_connection(child_agent, -1, - params.connect_domain, params.connect_port); -} - -void handle_service_refused(struct msg_header *hdr) -{ - struct service_params params; - int socket_fd; - - if (hdr->len != sizeof(params)) { - fprintf(stderr, "Invalid msg 0x%x length (%d)\n", MSG_SERVICE_REFUSED, hdr->len); - exit(1); - } - - if (libvchan_recv(ctrl_vchan, ¶ms, sizeof(params)) < 0) - handle_vchan_error("read exec params"); - - if (sscanf(params.ident, "SOCKET%d", &socket_fd)) - close(socket_fd); - else - fprintf(stderr, "Received REFUSED for unknown service request '%s'\n", params.ident); -} - -void handle_server_cmd() -{ - struct msg_header s_hdr; - - if (libvchan_recv(ctrl_vchan, &s_hdr, sizeof(s_hdr)) < 0) - handle_vchan_error("read 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_EXEC_CMDLINE: - case MSG_JUST_EXEC: - case MSG_SERVICE_CONNECT: - wake_meminfo_writer(); - handle_server_exec_request_init(&s_hdr); - break; - case MSG_SERVICE_REFUSED: - handle_service_refused(&s_hdr); - break; - default: - fprintf(stderr, "msg type from daemon is %d ?\n", - s_hdr.type); - exit(1); - } -} - -volatile int child_exited; - -void sigchld_handler(int x __attribute__((__unused__))) -{ - child_exited = 1; - signal(SIGCHLD, sigchld_handler); -} - -int find_connection(int pid) -{ - int i; - for (i = 0; i < MAX_FDS; i++) - if (connection_info[i].pid == pid) - return i; - return -1; -} - -void release_connection(int id) { - struct msg_header hdr; - struct exec_params params; - - hdr.type = MSG_CONNECTION_TERMINATED; - hdr.len = sizeof(struct exec_params); - params.connect_domain = connection_info[id].connect_domain; - params.connect_port = connection_info[id].connect_port; - if (libvchan_send(ctrl_vchan, &hdr, sizeof(hdr)) < 0) - handle_vchan_error("send"); - if (libvchan_send(ctrl_vchan, ¶ms, sizeof(params)) < 0) - handle_vchan_error("send"); - connection_info[id].pid = 0; -} - -void reap_children() -{ - int status; - int pid; - int id; - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { - if (pid == wait_for_session_pid) { - for (id = 0; id < MAX_FDS; id++) { - if (!requests_waiting_for_session[id].cmdline) - continue; - handle_server_exec_request_do( - requests_waiting_for_session[id].type, - requests_waiting_for_session[id].connect_domain, - requests_waiting_for_session[id].connect_port, - requests_waiting_for_session[id].cmdline); - free(requests_waiting_for_session[id].cmdline); - requests_waiting_for_session[id].cmdline = NULL; - } - wait_for_session_pid = -1; - continue; - } - id = find_connection(pid); - if (id < 0) - continue; - release_connection(id); - } - child_exited = 0; -} - -int fill_fds_for_select(fd_set * rdset, fd_set * wrset) -{ - int max = -1; - int i; - FD_ZERO(rdset); - FD_ZERO(wrset); - - FD_SET(trigger_fd, rdset); - if (trigger_fd > max) - max = trigger_fd; - - for (i = 0; i < MAX_FDS; i++) { - if (connection_info[i].pid != 0 && connection_info[i].fd != -1) { - FD_SET(connection_info[i].fd, rdset); - if (connection_info[i].fd > max) - max = connection_info[i].fd; - } - } - return max; -} - -void handle_trigger_io() -{ - struct msg_header hdr; - struct trigger_service_params params; - int ret; - int client_fd; - - client_fd = do_accept(trigger_fd); - if (client_fd < 0) - return; - hdr.len = sizeof(params); - ret = read(client_fd, ¶ms, sizeof(params)); - if (ret == sizeof(params)) { - hdr.type = MSG_TRIGGER_SERVICE; - snprintf(params.request_id.ident, sizeof(params.request_id), "SOCKET%d", client_fd); - if (libvchan_send(ctrl_vchan, &hdr, sizeof(hdr)) < 0) - handle_vchan_error("write hdr"); - if (libvchan_send(ctrl_vchan, ¶ms, sizeof(params)) < 0) - handle_vchan_error("write params"); - } - if (ret <= 0) { - close(client_fd); - } - /* do not close client_fd - we'll need it to send the connection details - * later (when dom0 accepts the request) */ -} - -void handle_terminated_fork_client(fd_set *rdset) { - int i, ret; - char buf[2]; - - for (i = 0; i < MAX_FDS; i++) { - if (connection_info[i].pid && connection_info[i].fd >= 0 && - FD_ISSET(connection_info[i].fd, rdset)) { - ret = read(connection_info[i].fd, buf, sizeof(buf)); - if (ret == 0 || (ret == -1 && errno == ECONNRESET)) { - close(connection_info[i].fd); - release_connection(i); - } else { - fprintf(stderr, "Unexpected read on fork-server connection: %d(%s)\n", ret, strerror(errno)); - close(connection_info[i].fd); - release_connection(i); - } - } - } -} - -int main() -{ - fd_set rdset, wrset; - int max; - 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 (libvchan_buffer_space(ctrl_vchan) <= - (int)sizeof(struct msg_header)) - FD_ZERO(&rdset); - - wait_for_vchan_or_argfd(ctrl_vchan, max, &rdset, &wrset); - sigprocmask(SIG_UNBLOCK, &chld_set, NULL); - - while (libvchan_data_ready(ctrl_vchan)) - handle_server_cmd(); - - if (FD_ISSET(trigger_fd, &rdset)) - handle_trigger_io(); - - handle_terminated_fork_client(&rdset); - } -} diff --git a/qrexec/qrexec-agent.h b/qrexec/qrexec-agent.h deleted file mode 100644 index 7cd11a2..0000000 --- a/qrexec/qrexec-agent.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * The Qubes OS Project, http://www.qubes-os.org - * - * Copyright (C) 2013 Marek Marczykowski-Górecki - * - * 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 QREXEC_FORK_SERVER_SOCKET "/var/run/qubes/qrexec-server.%s.sock" - -// directory for services configuration (for example 'wait-for-session' flag) -#define QUBES_RPC_CONFIG_DIR "/etc/qubes/rpc-config" -// support only very small configuration files, -#define MAX_CONFIG_SIZE 4096 - -int handle_handshake(libvchan_t *ctrl); -void handle_vchan_error(const char *op); -void do_exec(char *cmd); -/* call before fork() for service handling process (either end) */ -void prepare_child_env(); - -pid_t handle_new_process(int type, - int connect_domain, int connect_port, - char *cmdline, int cmdline_len); -int handle_data_client(int type, - int connect_domain, int connect_port, - int stdin_fd, int stdout_fd, int stderr_fd, - int buffer_size); - - -struct qrexec_cmd_info { - int type; - int connect_domain; - int connect_port; - int cmdline_len; - char cmdline[0]; -}; diff --git a/qrexec/qrexec-client-vm.c b/qrexec/qrexec-client-vm.c deleted file mode 100644 index 0065d00..0000000 --- a/qrexec/qrexec-client-vm.c +++ /dev/null @@ -1,224 +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 -#include -#include "libqrexec-utils.h" -#include "qrexec.h" -#include "qrexec-agent.h" - -void handle_vchan_error(const char *op) -{ - fprintf(stderr, "Error while vchan %s, exiting\n", op); - exit(1); -} - -void do_exec(char *cmd __attribute__((__unused__))) { - fprintf(stderr, "BUG: do_exec function shouldn't be called!\n"); - exit(1); -} - -int connect_unix_socket(char *path) -{ - 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, path, - sizeof(remote.sun_path) - 1); - 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; -} - -/* Target specification with keyword have changed from $... to @... . Convert - * the argument appropriately, to avoid breaking user tools. - */ -void convert_target_name_keyword(char *target) -{ - size_t i; - size_t len = strlen(target); - - for (i = 0; i < len; i++) - if (target[i] == '$') - target[i] = '@'; -} - -struct option longopts[] = { - { "buffer-size", required_argument, 0, 'b' }, - { NULL, 0, 0, 0}, -}; - -_Noreturn void usage(const char *argv0) { - fprintf(stderr, - "usage: %s [--buffer-size=BUFFER_SIZE] target_vmname program_ident [local_program [local program arguments]]\n", - argv0); - fprintf(stderr, "BUFFER_SIZE is minimum vchan buffer size (default: 64k)\n"); - exit(2); -} - -int main(int argc, char **argv) -{ - int trigger_fd; - struct trigger_service_params params; - struct exec_params exec_params; - int ret, i; - int start_local_process = 0; - char *abs_exec_path; - pid_t child_pid = 0; - int inpipe[2], outpipe[2]; - int buffer_size = 0; - int opt; - - while (1) { - opt = getopt_long(argc, argv, "+", longopts, NULL); - if (opt == -1) - break; - switch (opt) { - case 'b': - buffer_size = atoi(optarg); - break; - case '?': - usage(argv[0]); - } - } - - if (argc - optind < 2) { - usage(argv[0]); - } - if (argc - optind > 2) { - start_local_process = 1; - } - - trigger_fd = connect_unix_socket(QREXEC_AGENT_TRIGGER_PATH); - - memset(¶ms, 0, sizeof(params)); - strncpy(params.service_name, argv[optind + 1], sizeof(params.service_name) - 1); - - convert_target_name_keyword(argv[optind]); - strncpy(params.target_domain, argv[optind], - sizeof(params.target_domain) - 1); - - snprintf(params.request_id.ident, - sizeof(params.request_id.ident), "SOCKET"); - - if (write(trigger_fd, ¶ms, sizeof(params)) < 0) { - perror("write to agent"); - exit(1); - } - ret = read(trigger_fd, &exec_params, sizeof(exec_params)); - if (ret == 0) { - fprintf(stderr, "Request refused\n"); - exit(126); - } - if (ret < 0 || ret != sizeof(exec_params)) { - perror("read"); - exit(1); - } - - if (start_local_process) { - if (socketpair(AF_UNIX, SOCK_STREAM, 0, inpipe) || - socketpair(AF_UNIX, SOCK_STREAM, 0, outpipe)) { - perror("socketpair"); - exit(1); - } - prepare_child_env(); - - switch (child_pid = fork()) { - case -1: - perror("fork"); - exit(-1); - case 0: - close(inpipe[1]); - close(outpipe[0]); - close(trigger_fd); - for (i = 0; i < 3; i++) { - if (i != 2 || getenv("PASS_LOCAL_STDERR")) { - char *env; - if (asprintf(&env, "SAVED_FD_%d=%d", i, dup(i)) < 0) { - perror("prepare SAVED_FD_"); - exit(1); - } - putenv(env); - } - } - - dup2(inpipe[0], 0); - dup2(outpipe[1], 1); - close(inpipe[0]); - close(outpipe[1]); - - abs_exec_path = strdup(argv[optind + 2]); - argv[optind + 2] = get_program_name(argv[optind + 2]); - execv(abs_exec_path, argv + optind + 2); - perror("execv"); - exit(-1); - } - close(inpipe[0]); - close(outpipe[1]); - - ret = handle_data_client(MSG_SERVICE_CONNECT, - exec_params.connect_domain, exec_params.connect_port, - inpipe[1], outpipe[0], -1, buffer_size); - } else { - ret = handle_data_client(MSG_SERVICE_CONNECT, - exec_params.connect_domain, exec_params.connect_port, - 1, 0, -1, buffer_size); - } - - close(trigger_fd); - if (start_local_process) { - if (waitpid(child_pid, &i, 0) != -1) { - if (WIFSIGNALED(i)) - ret = 128 + WTERMSIG(i); - else - ret = WEXITSTATUS(i); - } else { - perror("wait for local process"); - } - } - - return ret; -} diff --git a/qrexec/qrexec-fork-server.c b/qrexec/qrexec-fork-server.c deleted file mode 100644 index 6c5eb98..0000000 --- a/qrexec/qrexec-fork-server.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * The Qubes OS Project, http://www.qubes-os.org - * - * Copyright (C) 2015 Marek Marczykowski-Górecki - * - * 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 -#include "qrexec.h" -#include -#include "libqrexec-utils.h" -#include "qrexec-agent.h" - -extern char **environ; - -void do_exec(char *cmd) -{ - char *shell; - - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); - - /* call QUBESRPC if requested */ - exec_qubes_rpc_if_requested(cmd, environ); - - /* otherwise, pass it to shell */ - shell = getenv("SHELL"); - if (!shell) - shell = "/bin/sh"; - - execl(shell, basename(shell), "-c", cmd, NULL); - perror("execl"); - exit(1); -} - -void handle_vchan_error(const char *op) -{ - fprintf(stderr, "Error while vchan %s, exiting\n", op); - exit(1); -} - -void handle_single_command(int fd, struct qrexec_cmd_info *info) { - char cmdline[info->cmdline_len+1]; - - if (!read_all(fd, cmdline, info->cmdline_len)) - return; - cmdline[info->cmdline_len] = 0; - - handle_new_process(info->type, info->connect_domain, - info->connect_port, - cmdline, info->cmdline_len); -} - -int main(int argc, char **argv) { - int s, fd; - char *socket_path; - struct qrexec_cmd_info info; - struct sockaddr_un peer; - unsigned int addrlen; - - - if (argc == 2) { - socket_path = argv[1]; - } else if (argc == 1) { - /* this will be leaked, but we don't care as the process will then terminate */ - if (asprintf(&socket_path, QREXEC_FORK_SERVER_SOCKET, getenv("USER")) < 0) { - fprintf(stderr, "Memory allocation failed\n"); - exit(1); - } - } else { - fprintf(stderr, "Usage: %s [socket path]\n", argv[0]); - exit(1); - } - - s = get_server_socket(socket_path); - if (fcntl(s, F_SETFD, O_CLOEXEC) < 0) { - perror("fcntl"); - exit(1); - } - /* fork into background */ - switch (fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - break; - default: - exit(0); - } - signal(SIGCHLD, SIG_IGN); - register_exec_func(do_exec); - - while (1) { - addrlen = sizeof(peer); - fd = accept(s, (struct sockaddr *) &peer, &addrlen); - if (fd < 0) - break; - if (read_all(fd, &info, sizeof(info))) { - handle_single_command(fd, &info); - } - close(fd); - } - close(s); - unlink(socket_path); - return 0; -} diff --git a/qrexec/qrexec.pam b/qrexec/qrexec.pam deleted file mode 100644 index c6896bc..0000000 --- a/qrexec/qrexec.pam +++ /dev/null @@ -1,9 +0,0 @@ -#%PAM-1.0 -auth sufficient pam_rootok.so -auth substack system-auth -auth include postlogin -account sufficient pam_succeed_if.so uid = 0 use_uid quiet -account include system-auth -password include system-auth -session include system-auth -session include postlogin diff --git a/qrexec/qrexec.pam.archlinux b/qrexec/qrexec.pam.archlinux deleted file mode 100644 index 3289b7c..0000000 --- a/qrexec/qrexec.pam.archlinux +++ /dev/null @@ -1,9 +0,0 @@ -#%PAM-1.0 -auth sufficient pam_rootok.so -# Uncomment the following line to implicitly trust users in the "wheel" group. -#auth sufficient pam_wheel.so trust use_uid -# Uncomment the following line to require a user to be in the "wheel" group. -#auth required pam_wheel.so use_uid -auth include system-login -account include system-login -session include system-login diff --git a/qrexec/qrexec.pam.debian b/qrexec/qrexec.pam.debian deleted file mode 100644 index 7ca3b20..0000000 --- a/qrexec/qrexec.pam.debian +++ /dev/null @@ -1,11 +0,0 @@ -#%PAM-1.0 -# -# based on /etc/pam.d/su -auth sufficient pam_rootok.so -session required pam_env.so readenv=1 -session required pam_env.so readenv=1 envfile=/etc/default/locale -session required pam_limits.so - -@include common-auth -@include common-account -@include common-session diff --git a/qrexec/qubes-rpc-multiplexer b/qrexec/qubes-rpc-multiplexer deleted file mode 100755 index c527a10..0000000 --- a/qrexec/qubes-rpc-multiplexer +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh -l - -# write stderr to both calling party and local log; be very careful about -# closing file descriptors here - if either stdout or stderr will not be closed -# when service process does the same - service call will hang (waiting for EOF -# on stdout/stderr) -stderr_pipe=/tmp/qrexec-rpc-stderr.$$ -mkfifo $stderr_pipe -# tee can't write to file descriptor, nor /proc/self/fd/2 (EXIO on open) -return_stderr_pipe=/tmp/qrexec-rpc-stderr-return.$$ -mkfifo $return_stderr_pipe -{ cat <$return_stderr_pipe >&2 2>/dev/null; rm -f $return_stderr_pipe; } & -{ tee $return_stderr_pipe 2>/dev/null <$stderr_pipe |\ - logger -t "$1-$2" >/dev/null 2>&1; rm -f $stderr_pipe; } & -exec 2>$stderr_pipe - -QUBES_RPC=/etc/qubes-rpc -LOCAL_QUBES_RPC=/usr/local/etc/qubes-rpc - -if ! [ $# = 2 ] ; then - echo "$0: bad argument count, usage: $0 SERVICE-NAME REMOTE-DOMAIN-NAME" >&2 - exit 1 -fi -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 - # shellcheck disable=SC2086 - exec "$CFG_FILE" ${QREXEC_SERVICE_ARGUMENT} - echo "$0: failed to execute handler for" "$1" >&2 - exit 1 -else - # shellcheck disable=SC2086 - exec /bin/sh -- "$CFG_FILE" ${QREXEC_SERVICE_ARGUMENT} - echo "$0: failed to execute handler for" "$1" >&2 - exit 1 -fi diff --git a/qubes-rpc/rpc-config.README b/qubes-rpc/rpc-config.README deleted file mode 100644 index 609f05f..0000000 --- a/qubes-rpc/rpc-config.README +++ /dev/null @@ -1,13 +0,0 @@ -This is directory for qrexec service additional configuration. Configuration -file needs to be named exactly as service. Configuration format is 'key=value' -(without spaces around '='). Lines starting with '#' are ignored. - -Supported settings: - -* wait-for-session - wait for full GUI session initialization before starting - the service. This is done using /etc/qubes-rpc/qubes.WaitForSession script. - There is no timeout - if the session is never initialized - (for example because there is no GUI running at all), service will never be - started. - Allowed values are 0 or 1. - diff --git a/rpm_spec/core-agent.spec.in b/rpm_spec/core-agent.spec.in index ac23eb6..7dbe104 100644 --- a/rpm_spec/core-agent.spec.in +++ b/rpm_spec/core-agent.spec.in @@ -20,7 +20,7 @@ # # -%define qubes_services qubes-core qubes-core-netvm qubes-core-early qubes-firewall qubes-iptables qubes-updates-proxy qubes-qrexec-agent qubes-updates-proxy-forwarder +%define qubes_services qubes-core qubes-core-netvm qubes-core-early qubes-firewall qubes-iptables qubes-updates-proxy qubes-updates-proxy-forwarder %define qubes_preset_file 75-qubes-vm.preset %define scriptletfuns is_static() { \ @@ -134,7 +134,7 @@ Requires: ImageMagick Requires: librsvg2-tools Requires: zenity Requires: dconf -Requires: qubes-core-agent-qrexec +Requires: qubes-core-qrexec-vm Requires: qubes-libvchan Requires: qubes-db-vm %if 0%{?fedora} >= 23 @@ -184,14 +184,6 @@ DNF plugin for Qubes specific post-installation actions: * notify dom0 that updates were installed * refresh applications shortcut list -%package qrexec -Summary: Qubes qrexec agent -Conflicts: qubes-core-vm < 4.0.0 - -%description qrexec -Agent part of Qubes RPC system. A daemon responsible for starting processes as -requested by dom0 or other VMs, according to dom0-enforced policy. - %package nautilus Summary: Qubes integration for Nautilus Requires: qubes-core-agent = %{version} @@ -271,7 +263,7 @@ Thunar support for Qubes VM tools %build %{?set_build_flags} -for dir in qubes-rpc qrexec misc; do +for dir in qubes-rpc misc; do make -C $dir BACKEND_VMM=@BACKEND_VMM@ done make -C doc manpages @@ -302,7 +294,6 @@ usermod -p '' root %install -(cd qrexec; make install DESTDIR=$RPM_BUILD_ROOT) make install-vm DESTDIR=$RPM_BUILD_ROOT %if 0%{?rhel} >= 7 @@ -469,9 +460,6 @@ sed 's/^net.ipv4.ip_forward.*/#\0/' -i /etc/sysctl.conf %systemd_post qubes-network.service %systemd_post qubes-updates-proxy.service -%post qrexec -%systemd_post qubes-qrexec-agent.service - %post thunar if [ "$1" = 1 ]; then # There is no system-wide Thunar custom actions. There is only a default @@ -506,9 +494,6 @@ fi %systemd_preun qubes-network.service %systemd_preun qubes-updates-proxy.service -%preun qrexec -%systemd_preun qubes-qrexec-agent.service - %postun thunar if [ "$1" = 0 ]; then if [ -f /etc/xdg/Thunar/uca.xml ] ; then @@ -581,8 +566,6 @@ rm -f %{name}-%{version} %config(noreplace) /etc/qubes-rpc/qubes.StartApp %config(noreplace) /etc/qubes-rpc/qubes.PostInstall %config(noreplace) /etc/qubes-rpc/qubes.GetDate -%dir /etc/qubes/rpc-config -/etc/qubes/rpc-config/README %config(noreplace) /etc/qubes/rpc-config/qubes.OpenInVM %config(noreplace) /etc/qubes/rpc-config/qubes.OpenURL %config(noreplace) /etc/qubes/rpc-config/qubes.SelectFile @@ -691,17 +674,6 @@ rm -f %{name}-%{version} %files -n python%{python3_pkgversion}-dnf-plugins-qubes-hooks %{python3_sitelib}/dnf-plugins/* -%files qrexec -%config(noreplace) /etc/pam.d/qrexec -/usr/bin/qrexec-fork-server -/usr/bin/qrexec-client-vm -/usr/lib/qubes/qrexec-agent -/usr/lib/qubes/qrexec-client-vm -/usr/lib/qubes/qrexec_client_vm -/usr/lib/qubes/qubes-rpc-multiplexer -/lib/systemd/system/qubes-qrexec-agent.service -%{_mandir}/man1/qrexec-client-vm.1* - %files nautilus /usr/lib/qubes/qvm-copy-to-vm.gnome /usr/lib/qubes/qvm-move-to-vm.gnome @@ -761,7 +733,7 @@ License: GPL v2 only Group: Qubes Requires: upstart Requires: qubes-core-agent = %{version} -Requires: qubes-core-agent-qrexec = %{version} +Requires: qubes-core-qrexec-vm Requires: qubes-core-agent-networking = %{version} Provides: qubes-core-agent-init-scripts Conflicts: qubes-core-agent-systemd @@ -779,7 +751,6 @@ The Qubes core startup configuration for SysV init (or upstart). /etc/init.d/qubes-firewall /etc/init.d/qubes-iptables /etc/init.d/qubes-updates-proxy -/etc/init.d/qubes-qrexec-agent /etc/init.d/qubes-updates-proxy-forwarder /etc/sysconfig/modules/qubes-core.modules diff --git a/vm-init.d/qubes-qrexec-agent b/vm-init.d/qubes-qrexec-agent deleted file mode 100755 index c70d3c6..0000000 --- a/vm-init.d/qubes-qrexec-agent +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# -# chkconfig: 345 90 90 -# description: Executes Qubes core scripts at VM boot -# -# Source function library. -# shellcheck disable=SC1091 -. /etc/rc.d/init.d/functions - -# Source Qubes library. -# shellcheck source=init/functions -. /usr/lib/qubes/init/functions - -start() -{ - have_qrexec_agent || return - echo -n $"Starting Qubes RPC agent:" - /usr/lib/qubes/qrexec-agent 2>/var/log/qubes/qrexec-agent.log & - success - echo "" - return 0 -} - -stop() -{ - have_qrexec_agent || return - killproc qrexec-agent -} - -case "$1" in - start) - start - ;; - stop) - stop - ;; - *) - echo $"Usage: $0 {start|stop}" - exit 3 - ;; -esac - -# shellcheck disable=SC2086 -exit $RETVAL diff --git a/vm-systemd/qubes-qrexec-agent.service b/vm-systemd/qubes-qrexec-agent.service deleted file mode 100644 index b09480a..0000000 --- a/vm-systemd/qubes-qrexec-agent.service +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=Qubes remote exec agent -After=xendriverdomain.service - -[Service] -Type=notify -ExecStartPre=/bin/sh -c '[ -e /dev/xen/evtchn ] || modprobe xen_evtchn' -ExecStart=/usr/lib/qubes/qrexec-agent - -[Install] -WantedBy=multi-user.target -Alias=qubes-core-agent.service