Browse Source

Remove qrexec-agent related files

Move it to the core-qrexec repository.

QubesOS/qubes-issues#4955
Marek Marczykowski-Górecki 5 years ago
parent
commit
20285bc6c2

+ 0 - 4
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

+ 5 - 18
debian/control

@@ -4,7 +4,6 @@ Priority: extra
 Maintainer: unman <unman@thirdeyesecurity.org>
 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.

+ 0 - 10
debian/qubes-core-agent-qrexec.install

@@ -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

+ 1 - 1
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

+ 0 - 1
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

+ 0 - 85
doc/vm-tools/qrexec-client-vm.rst

@@ -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 <joanna at invisiblethingslab dot com>
-| Rafal Wojtczuk <rafal at invisiblethingslab dot com>
-| Marek Marczykowski-Górecki <marmarek at invisiblethingslab dot com>

+ 0 - 31
qrexec/Makefile

@@ -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
-

+ 0 - 594
qrexec/qrexec-agent-data.c

@@ -1,594 +0,0 @@
-/*
- * The Qubes OS Project, http://www.qubes-os.org
- *
- * Copyright (C) 2013  Marek Marczykowski-Górecki  <marmarek@invisiblethingslab.com>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <fcntl.h>
-#include <libvchan.h>
-#include <assert.h>
-#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;
-}

+ 0 - 948
qrexec/qrexec-agent.c

@@ -1,948 +0,0 @@
-/*
- * The Qubes OS Project, http://www.qubes-os.org
- *
- * Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
- *
- * 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 <sys/select.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <unistd.h>
-#include <errno.h>
-#include <err.h>
-#include <sys/wait.h>
-#include <fcntl.h>
-#include <string.h>
-#include <pwd.h>
-#include <grp.h>
-#include <sys/stat.h>
-#include <assert.h>
-#ifdef HAVE_PAM
-#include <security/pam_appl.h>
-#endif
-#include "qrexec.h"
-#include <libvchan.h>
-#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; i<num_msg; i++) {
-        if (msg[i]->msg_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, &params, 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, &params, 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, &params, 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, &params, 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, &params, 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, &params, 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);
-    }
-}

+ 0 - 50
qrexec/qrexec-agent.h

@@ -1,50 +0,0 @@
-/*
- * The Qubes OS Project, http://www.qubes-os.org
- *
- * Copyright (C) 2013  Marek Marczykowski-Górecki  <marmarek@invisiblethingslab.com>
- *
- * 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];
-};

+ 0 - 224
qrexec/qrexec-client-vm.c

@@ -1,224 +0,0 @@
-/*
- * The Qubes OS Project, http://www.qubes-os.org
- *
- * Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
- *
- * 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 <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/un.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <getopt.h>
-#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(&params, 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, &params, 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;
-}

+ 0 - 128
qrexec/qrexec-fork-server.c

@@ -1,128 +0,0 @@
-/*
- * The Qubes OS Project, http://www.qubes-os.org
- *
- * Copyright (C) 2015  Marek Marczykowski-Górecki  <marmarek@invisiblethingslab.com>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include "qrexec.h"
-#include <libvchan.h>
-#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;
-}

+ 0 - 9
qrexec/qrexec.pam

@@ -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

+ 0 - 9
qrexec/qrexec.pam.archlinux

@@ -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

+ 0 - 11
qrexec/qrexec.pam.debian

@@ -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

+ 0 - 49
qrexec/qubes-rpc-multiplexer

@@ -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

+ 0 - 13
qubes-rpc/rpc-config.README

@@ -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.
-

+ 4 - 33
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
 

+ 0 - 44
vm-init.d/qubes-qrexec-agent

@@ -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

+ 0 - 12
vm-systemd/qubes-qrexec-agent.service

@@ -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