Merge branch 'remove-qrexec'
* remove-qrexec: travis: update for R4.1 Remove qrexec-agent related files
This commit is contained in:
commit
08a853b960
14
.travis.yml
14
.travis.yml
@ -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
|
||||
|
||||
|
||||
|
4
Makefile
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 -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
23
debian/control
vendored
@ -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.
|
||||
|
10
debian/qubes-core-agent-qrexec.install
vendored
10
debian/qubes-core-agent-qrexec.install
vendored
@ -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
|
2
debian/qubes-core-agent.postrm
vendored
2
debian/qubes-core-agent.postrm
vendored
@ -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
1
debian/rules
vendored
@ -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
|
||||
|
@ -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>
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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, ¶ms, sizeof(params)) < 0)
|
||||
handle_vchan_error("read exec params");
|
||||
if (libvchan_recv(ctrl_vchan, buf, buf_len) < 0)
|
||||
handle_vchan_error("read exec cmd");
|
||||
|
||||
buf[buf_len-1] = 0;
|
||||
|
||||
if (hdr->type != MSG_SERVICE_CONNECT && wait_for_session_maybe(buf)) {
|
||||
/* waiting for session, postpone actual call */
|
||||
int slot_index;
|
||||
for (slot_index = 0; slot_index < MAX_FDS; slot_index++)
|
||||
if (!requests_waiting_for_session[slot_index].cmdline)
|
||||
break;
|
||||
if (slot_index == MAX_FDS) {
|
||||
/* no free slots */
|
||||
fprintf(stderr, "No free slots for waiting for GUI session, continuing!\n");
|
||||
} else {
|
||||
requests_waiting_for_session[slot_index].type = hdr->type;
|
||||
requests_waiting_for_session[slot_index].connect_domain = params.connect_domain;
|
||||
requests_waiting_for_session[slot_index].connect_port = params.connect_port;
|
||||
requests_waiting_for_session[slot_index].cmdline = strdup(buf);
|
||||
/* nothing to do now, when we get GUI session, we'll continue */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
handle_server_exec_request_do(hdr->type, params.connect_domain, params.connect_port, buf);
|
||||
}
|
||||
|
||||
void handle_server_exec_request_do(int type, int connect_domain, int connect_port, char *cmdline) {
|
||||
int client_fd;
|
||||
pid_t child_agent;
|
||||
int cmdline_len = strlen(cmdline) + 1; // size of cmdline, including \0 at the end
|
||||
struct exec_params params = {
|
||||
.connect_domain = connect_domain,
|
||||
.connect_port = connect_port,
|
||||
};
|
||||
|
||||
if ((type == MSG_EXEC_CMDLINE || type == MSG_JUST_EXEC) &&
|
||||
!strstr(cmdline, ":nogui:")) {
|
||||
int child_socket;
|
||||
|
||||
child_socket = try_fork_server(type,
|
||||
params.connect_domain, params.connect_port,
|
||||
cmdline, cmdline_len);
|
||||
if (child_socket >= 0) {
|
||||
register_vchan_connection(-1, child_socket,
|
||||
params.connect_domain, params.connect_port);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == MSG_SERVICE_CONNECT && sscanf(cmdline, "SOCKET%d", &client_fd)) {
|
||||
/* FIXME: Maybe add some check if client_fd is really FD to some
|
||||
* qrexec-client-vm process; but this data comes from qrexec-daemon
|
||||
* (which sends back what it got from us earlier), so it isn't critical.
|
||||
*/
|
||||
if (write(client_fd, ¶ms, sizeof(params)) < 0) {
|
||||
/* ignore */
|
||||
}
|
||||
/* No need to send request_id (buf) - the client don't need it, there
|
||||
* is only meaningless (for the client) socket FD */
|
||||
/* Register connection even if there was an error sending params to
|
||||
* qrexec-client-vm. This way the mainloop will clean the things up
|
||||
* (close socket, send MSG_CONNECTION_TERMINATED) when qrexec-client-vm
|
||||
* will close the socket (terminate itself). */
|
||||
register_vchan_connection(-1, client_fd,
|
||||
params.connect_domain, params.connect_port);
|
||||
return;
|
||||
}
|
||||
|
||||
/* No fork server case */
|
||||
child_agent = handle_new_process(type,
|
||||
params.connect_domain, params.connect_port,
|
||||
cmdline, cmdline_len);
|
||||
|
||||
register_vchan_connection(child_agent, -1,
|
||||
params.connect_domain, params.connect_port);
|
||||
}
|
||||
|
||||
void handle_service_refused(struct msg_header *hdr)
|
||||
{
|
||||
struct service_params params;
|
||||
int socket_fd;
|
||||
|
||||
if (hdr->len != sizeof(params)) {
|
||||
fprintf(stderr, "Invalid msg 0x%x length (%d)\n", MSG_SERVICE_REFUSED, hdr->len);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (libvchan_recv(ctrl_vchan, ¶ms, sizeof(params)) < 0)
|
||||
handle_vchan_error("read exec params");
|
||||
|
||||
if (sscanf(params.ident, "SOCKET%d", &socket_fd))
|
||||
close(socket_fd);
|
||||
else
|
||||
fprintf(stderr, "Received REFUSED for unknown service request '%s'\n", params.ident);
|
||||
}
|
||||
|
||||
void handle_server_cmd()
|
||||
{
|
||||
struct msg_header s_hdr;
|
||||
|
||||
if (libvchan_recv(ctrl_vchan, &s_hdr, sizeof(s_hdr)) < 0)
|
||||
handle_vchan_error("read s_hdr");
|
||||
|
||||
// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.client_id,
|
||||
// s_hdr.len);
|
||||
|
||||
switch (s_hdr.type) {
|
||||
case MSG_EXEC_CMDLINE:
|
||||
case MSG_JUST_EXEC:
|
||||
case MSG_SERVICE_CONNECT:
|
||||
wake_meminfo_writer();
|
||||
handle_server_exec_request_init(&s_hdr);
|
||||
break;
|
||||
case MSG_SERVICE_REFUSED:
|
||||
handle_service_refused(&s_hdr);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "msg type from daemon is %d ?\n",
|
||||
s_hdr.type);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
volatile int child_exited;
|
||||
|
||||
void sigchld_handler(int x __attribute__((__unused__)))
|
||||
{
|
||||
child_exited = 1;
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
}
|
||||
|
||||
int find_connection(int pid)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_FDS; i++)
|
||||
if (connection_info[i].pid == pid)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void release_connection(int id) {
|
||||
struct msg_header hdr;
|
||||
struct exec_params params;
|
||||
|
||||
hdr.type = MSG_CONNECTION_TERMINATED;
|
||||
hdr.len = sizeof(struct exec_params);
|
||||
params.connect_domain = connection_info[id].connect_domain;
|
||||
params.connect_port = connection_info[id].connect_port;
|
||||
if (libvchan_send(ctrl_vchan, &hdr, sizeof(hdr)) < 0)
|
||||
handle_vchan_error("send");
|
||||
if (libvchan_send(ctrl_vchan, ¶ms, sizeof(params)) < 0)
|
||||
handle_vchan_error("send");
|
||||
connection_info[id].pid = 0;
|
||||
}
|
||||
|
||||
void reap_children()
|
||||
{
|
||||
int status;
|
||||
int pid;
|
||||
int id;
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
||||
if (pid == wait_for_session_pid) {
|
||||
for (id = 0; id < MAX_FDS; id++) {
|
||||
if (!requests_waiting_for_session[id].cmdline)
|
||||
continue;
|
||||
handle_server_exec_request_do(
|
||||
requests_waiting_for_session[id].type,
|
||||
requests_waiting_for_session[id].connect_domain,
|
||||
requests_waiting_for_session[id].connect_port,
|
||||
requests_waiting_for_session[id].cmdline);
|
||||
free(requests_waiting_for_session[id].cmdline);
|
||||
requests_waiting_for_session[id].cmdline = NULL;
|
||||
}
|
||||
wait_for_session_pid = -1;
|
||||
continue;
|
||||
}
|
||||
id = find_connection(pid);
|
||||
if (id < 0)
|
||||
continue;
|
||||
release_connection(id);
|
||||
}
|
||||
child_exited = 0;
|
||||
}
|
||||
|
||||
int fill_fds_for_select(fd_set * rdset, fd_set * wrset)
|
||||
{
|
||||
int max = -1;
|
||||
int i;
|
||||
FD_ZERO(rdset);
|
||||
FD_ZERO(wrset);
|
||||
|
||||
FD_SET(trigger_fd, rdset);
|
||||
if (trigger_fd > max)
|
||||
max = trigger_fd;
|
||||
|
||||
for (i = 0; i < MAX_FDS; i++) {
|
||||
if (connection_info[i].pid != 0 && connection_info[i].fd != -1) {
|
||||
FD_SET(connection_info[i].fd, rdset);
|
||||
if (connection_info[i].fd > max)
|
||||
max = connection_info[i].fd;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
void handle_trigger_io()
|
||||
{
|
||||
struct msg_header hdr;
|
||||
struct trigger_service_params params;
|
||||
int ret;
|
||||
int client_fd;
|
||||
|
||||
client_fd = do_accept(trigger_fd);
|
||||
if (client_fd < 0)
|
||||
return;
|
||||
hdr.len = sizeof(params);
|
||||
ret = read(client_fd, ¶ms, sizeof(params));
|
||||
if (ret == sizeof(params)) {
|
||||
hdr.type = MSG_TRIGGER_SERVICE;
|
||||
snprintf(params.request_id.ident, sizeof(params.request_id), "SOCKET%d", client_fd);
|
||||
if (libvchan_send(ctrl_vchan, &hdr, sizeof(hdr)) < 0)
|
||||
handle_vchan_error("write hdr");
|
||||
if (libvchan_send(ctrl_vchan, ¶ms, sizeof(params)) < 0)
|
||||
handle_vchan_error("write params");
|
||||
}
|
||||
if (ret <= 0) {
|
||||
close(client_fd);
|
||||
}
|
||||
/* do not close client_fd - we'll need it to send the connection details
|
||||
* later (when dom0 accepts the request) */
|
||||
}
|
||||
|
||||
void handle_terminated_fork_client(fd_set *rdset) {
|
||||
int i, ret;
|
||||
char buf[2];
|
||||
|
||||
for (i = 0; i < MAX_FDS; i++) {
|
||||
if (connection_info[i].pid && connection_info[i].fd >= 0 &&
|
||||
FD_ISSET(connection_info[i].fd, rdset)) {
|
||||
ret = read(connection_info[i].fd, buf, sizeof(buf));
|
||||
if (ret == 0 || (ret == -1 && errno == ECONNRESET)) {
|
||||
close(connection_info[i].fd);
|
||||
release_connection(i);
|
||||
} else {
|
||||
fprintf(stderr, "Unexpected read on fork-server connection: %d(%s)\n", ret, strerror(errno));
|
||||
close(connection_info[i].fd);
|
||||
release_connection(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
fd_set rdset, wrset;
|
||||
int max;
|
||||
sigset_t chld_set;
|
||||
|
||||
init();
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
sigemptyset(&chld_set);
|
||||
sigaddset(&chld_set, SIGCHLD);
|
||||
|
||||
|
||||
for (;;) {
|
||||
sigprocmask(SIG_BLOCK, &chld_set, NULL);
|
||||
if (child_exited)
|
||||
reap_children();
|
||||
max = fill_fds_for_select(&rdset, &wrset);
|
||||
if (libvchan_buffer_space(ctrl_vchan) <=
|
||||
(int)sizeof(struct msg_header))
|
||||
FD_ZERO(&rdset);
|
||||
|
||||
wait_for_vchan_or_argfd(ctrl_vchan, max, &rdset, &wrset);
|
||||
sigprocmask(SIG_UNBLOCK, &chld_set, NULL);
|
||||
|
||||
while (libvchan_data_ready(ctrl_vchan))
|
||||
handle_server_cmd();
|
||||
|
||||
if (FD_ISSET(trigger_fd, &rdset))
|
||||
handle_trigger_io();
|
||||
|
||||
handle_terminated_fork_client(&rdset);
|
||||
}
|
||||
}
|
@ -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];
|
||||
};
|
@ -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(¶ms, 0, sizeof(params));
|
||||
strncpy(params.service_name, argv[optind + 1], sizeof(params.service_name) - 1);
|
||||
|
||||
convert_target_name_keyword(argv[optind]);
|
||||
strncpy(params.target_domain, argv[optind],
|
||||
sizeof(params.target_domain) - 1);
|
||||
|
||||
snprintf(params.request_id.ident,
|
||||
sizeof(params.request_id.ident), "SOCKET");
|
||||
|
||||
if (write(trigger_fd, ¶ms, sizeof(params)) < 0) {
|
||||
perror("write to agent");
|
||||
exit(1);
|
||||
}
|
||||
ret = read(trigger_fd, &exec_params, sizeof(exec_params));
|
||||
if (ret == 0) {
|
||||
fprintf(stderr, "Request refused\n");
|
||||
exit(126);
|
||||
}
|
||||
if (ret < 0 || ret != sizeof(exec_params)) {
|
||||
perror("read");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (start_local_process) {
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inpipe) ||
|
||||
socketpair(AF_UNIX, SOCK_STREAM, 0, outpipe)) {
|
||||
perror("socketpair");
|
||||
exit(1);
|
||||
}
|
||||
prepare_child_env();
|
||||
|
||||
switch (child_pid = fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(-1);
|
||||
case 0:
|
||||
close(inpipe[1]);
|
||||
close(outpipe[0]);
|
||||
close(trigger_fd);
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (i != 2 || getenv("PASS_LOCAL_STDERR")) {
|
||||
char *env;
|
||||
if (asprintf(&env, "SAVED_FD_%d=%d", i, dup(i)) < 0) {
|
||||
perror("prepare SAVED_FD_");
|
||||
exit(1);
|
||||
}
|
||||
putenv(env);
|
||||
}
|
||||
}
|
||||
|
||||
dup2(inpipe[0], 0);
|
||||
dup2(outpipe[1], 1);
|
||||
close(inpipe[0]);
|
||||
close(outpipe[1]);
|
||||
|
||||
abs_exec_path = strdup(argv[optind + 2]);
|
||||
argv[optind + 2] = get_program_name(argv[optind + 2]);
|
||||
execv(abs_exec_path, argv + optind + 2);
|
||||
perror("execv");
|
||||
exit(-1);
|
||||
}
|
||||
close(inpipe[0]);
|
||||
close(outpipe[1]);
|
||||
|
||||
ret = handle_data_client(MSG_SERVICE_CONNECT,
|
||||
exec_params.connect_domain, exec_params.connect_port,
|
||||
inpipe[1], outpipe[0], -1, buffer_size);
|
||||
} else {
|
||||
ret = handle_data_client(MSG_SERVICE_CONNECT,
|
||||
exec_params.connect_domain, exec_params.connect_port,
|
||||
1, 0, -1, buffer_size);
|
||||
}
|
||||
|
||||
close(trigger_fd);
|
||||
if (start_local_process) {
|
||||
if (waitpid(child_pid, &i, 0) != -1) {
|
||||
if (WIFSIGNALED(i))
|
||||
ret = 128 + WTERMSIG(i);
|
||||
else
|
||||
ret = WEXITSTATUS(i);
|
||||
} else {
|
||||
perror("wait for local process");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user