Merge branch 'remove-qrexec'

* remove-qrexec:
  travis: update for R4.1
  Remove qrexec-agent related files
This commit is contained in:
Marek Marczykowski-Górecki 2019-06-06 23:20:11 +02:00
commit 08a853b960
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
21 changed files with 16 additions and 2282 deletions

View File

@ -5,13 +5,11 @@ python: '3.5'
install: git clone https://github.com/QubesOS/qubes-builder ~/qubes-builder
script: ~/qubes-builder/scripts/travis-build
env:
- DISTS_VM=fc28 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1
- DISTS_VM=fc29 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1
- DISTS_VM=fc30 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1
- DISTS_VM=jessie USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1
- DISTS_VM=stretch USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1
- DISTS_VM=buster USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1
- DISTS_VM=centos7 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1
- DISTS_VM=fc29 USE_QUBES_REPO_VERSION=4.1 USE_QUBES_REPO_TESTING=1
- DISTS_VM=fc30 USE_QUBES_REPO_VERSION=4.1 USE_QUBES_REPO_TESTING=1
- DISTS_VM=stretch USE_QUBES_REPO_VERSION=4.1 USE_QUBES_REPO_TESTING=1
- DISTS_VM=buster USE_QUBES_REPO_VERSION=4.1 USE_QUBES_REPO_TESTING=1
- DISTS_VM=centos7 USE_QUBES_REPO_VERSION=4.1 USE_QUBES_REPO_TESTING=1
jobs:
include:
@ -25,7 +23,7 @@ jobs:
- codecov
- stage: deploy
python: '3.5'
env: DIST_DOM0=fc25 TESTS_ONLY=
env: DIST_DOM0=fc29 TESTS_ONLY=
script: ~/qubes-builder/scripts/travis-deploy

View File

@ -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 -m 0755 qubes-rpc/qubes.ShowInTerminal $(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

23
debian/control vendored
View File

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

View File

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

View File

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

1
debian/rules vendored
View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -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];
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
@ -582,8 +567,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
@ -694,17 +677,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
@ -764,7 +736,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
@ -782,7 +754,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

View File

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

View File

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