diff --git a/.gitignore b/.gitignore index cc81005..b1e3923 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ deb/* *.o .coverage *.egg-info +__pycache__ diff --git a/.travis.yml b/.travis.yml index 1a762f1..154f76a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +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=fc24 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1 - - DISTS_VM=fc25 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1 - DISTS_VM=fc26 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1 + - DISTS_VM=fc27 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=centos7 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1 jobs: include: diff --git a/Makefile b/Makefile index 388d658..a99ef4d 100644 --- a/Makefile +++ b/Makefile @@ -5,15 +5,16 @@ VERSION := $(shell cat version) DIST ?= fc18 KDESERVICEDIR ?= /usr/share/kde4/services KDE5SERVICEDIR ?= /usr/share/kservices5/ServiceMenus/ +APPLICATIONSDIR ?= /usr/share/applications SBINDIR ?= /usr/sbin BINDIR ?= /usr/bin LIBDIR ?= /usr/lib SYSLIBDIR ?= /lib PYTHON ?= /usr/bin/python2 -PYTHON_SITEARCH = `python2 -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_lib(1)'` -PYTHON2_SITELIB = `python2 -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_lib()'` -PYTHON3_SITELIB = `python3 -c 'import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())'` +PYTHON_SITEARCH = $(shell python2 -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_lib(1)') +PYTHON2_SITELIB = $(shell python2 -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_lib()') +PYTHON3_SITELIB = $(shell python3 -c 'import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())') # This makefile uses some bash-isms, make uses /bin/sh by default. SHELL = /bin/bash @@ -46,6 +47,12 @@ clean: make -C misc clean make -C qrexec clean make -C qubes-rpc clean + make -C doc clean + rm -rf qubesagent/*.pyc qubesagent/__pycache__ + rm -rf test-packages/__pycache__ + rm -rf test-packages/qubesagent.egg-info + rm -rf __pycache__ + rm -f .coverage all: make -C misc @@ -77,6 +84,10 @@ ifeq ($(shell lsb_release -is), Ubuntu) SYSTEM_DROPINS := $(strip $(patsubst crond.service, cron.service, $(SYSTEM_DROPINS))) SYSTEM_DROPINS += anacron.service SYSTEM_DROPINS += anacron-resume.service + SYSTEM_DROPINS += netfilter-persistent.service + SYSTEM_DROPINS += exim4.service + SYSTEM_DROPINS += avahi-daemon.service + endif # Debian Dropins @@ -208,7 +219,7 @@ install-common: install-doc misc/20_org.mate.NotificationDaemon.qubes.gschema.override \ misc/20_org.gnome.desktop.wm.preferences.qubes.gschema.override \ $(DESTDIR)/usr/share/glib-2.0/schemas/ - install -g user -m 2775 -d $(DESTDIR)/var/lib/qubes/dom0-updates + install -m 2775 -d $(DESTDIR)/var/lib/qubes/dom0-updates install -D -m 0644 misc/qubes-master-key.asc $(DESTDIR)/usr/share/qubes/qubes-master-key.asc install misc/resize-rootfs $(DESTDIR)$(LIBDIR)/qubes/ @@ -223,6 +234,8 @@ install-common: install-doc install -d $(DESTDIR)$(BINDIR) install -m 0755 misc/qubes-session-autostart $(DESTDIR)$(BINDIR)/qubes-session-autostart install -m 0755 misc/qvm-features-request $(DESTDIR)$(BINDIR)/qvm-features-request + install -m 0755 misc/qubes-run-terminal $(DESTDIR)/$(BINDIR) + install -D -m 0755 misc/qubes-run-terminal.desktop $(DESTDIR)/$(APPLICATIONSDIR)/qubes-run-terminal.desktop install -m 0755 qubes-rpc/qvm-sync-clock $(DESTDIR)$(BINDIR)/qvm-sync-clock install qubes-rpc/{qvm-open-in-dvm,qvm-open-in-vm,qvm-copy-to-vm,qvm-run-vm} $(DESTDIR)/usr/bin install qubes-rpc/qvm-copy $(DESTDIR)/usr/bin @@ -287,23 +300,10 @@ install-common: install-doc install -d $(DESTDIR)/usr/share/nautilus-python/extensions install -m 0644 qubes-rpc/*_nautilus.py $(DESTDIR)/usr/share/nautilus-python/extensions -ifeq ($(findstring CentOS,$(shell cat /etc/redhat-release)),) - install -D -m 0644 misc/dconf-profile-user $(DESTDIR)/etc/dconf/profile/user install -D -m 0644 misc/dconf-db-local-dpi $(DESTDIR)/etc/dconf/db/local.d/dpi -endif install -D -m 0755 misc/qubes-desktop-run $(DESTDIR)$(BINDIR)/qubes-desktop-run - mkdir -p $(DESTDIR)/$(PYTHON_SITEARCH)/qubes/ - -ifeq ($(shell lsb_release -is), Debian) - install -m 0644 misc/qubesxdg.py $(DESTDIR)/$(PYTHON2_SITELIB)/ -else ifeq ($(shell lsb_release -is), Ubuntu) - install -m 0644 misc/qubesxdg.py $(DESTDIR)/$(PYTHON2_SITELIB)/ -else - install -m 0644 misc/py2/qubesxdg.py* $(DESTDIR)/$(PYTHON2_SITELIB)/ -endif - install -d $(DESTDIR)/mnt/removable install -D -m 0644 misc/xorg-preload-apps.conf $(DESTDIR)/etc/X11/xorg-preload-apps.conf diff --git a/archlinux/PKGBUILD.install b/archlinux/PKGBUILD.install index 3438c17..258756b 100644 --- a/archlinux/PKGBUILD.install +++ b/archlinux/PKGBUILD.install @@ -82,6 +82,14 @@ update_qubesconfig() { #/usr/lib/qubes/update-proxy-configs # Archlinux pacman configuration is handled in update_finalize + if ! [ -r /etc/dconf/profile/user ]; then + mkdir -p /etc/dconf/profile + echo "user-db:user" >> /etc/dconf/profile/user + echo "system-db:local" >> /etc/dconf/profile/user + fi + + dconf update &> /dev/null || : + # Location of files which contains list of protected files mkdir -p /etc/qubes/protected-files.d # shellcheck source=init/functions @@ -324,6 +332,8 @@ post_install() { cp /etc/init/serial.conf /var/lib/qubes/serial.orig fi + chgrp user /var/lib/qubes/dom0-updates + # Remove most of the udev scripts to speed up the VM boot time # Just leave the xen* scripts, that are needed if this VM was # ever used as a net backend (e.g. as a VPN domain in the future) diff --git a/debian/changelog b/debian/changelog index a1549fd..81b2ff7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,139 @@ +qubes-core-agent (4.0.31-1) unstable; urgency=medium + + * debian: add Depends: qubesdb-vm + + -- Marek Marczykowski-Górecki Fri, 15 Jun 2018 14:32:23 +0200 + +qubes-core-agent (4.0.30-1) wheezy; urgency=medium + + [ Christopher Laprise ] + * Fixes issue #3939 + + -- Marek Marczykowski-Górecki Tue, 05 Jun 2018 01:39:04 +0200 + +qubes-core-agent (4.0.29-1) unstable; urgency=medium + + * Drop leftovers of qubes-netwatcher service + * qrexec: fix handling remote domain death + * network: use iptables-restore --wait if available + * rpm: add BR: systemd for pre/post install macros + * qubes-rpc: fix code style - indent with spaces + * qvm-open-in-vm: implement --view-only option + * qvm-open-in-vm: mark file as read-only if opened with --view-only + * Add file managers integration for qvm-open-in-dvm --view-only + * Add build-time assert for filename buffer size + + -- Marek Marczykowski-Górecki Tue, 29 May 2018 00:40:11 +0200 + +qubes-core-agent (4.0.28-1) wheezy; urgency=medium + + [ Peter Gerber ] + * Qubes firewall: correct syntax for icmpv6 rejects + + -- Marek Marczykowski-Górecki Thu, 10 May 2018 12:21:39 +0200 + +qubes-core-agent (4.0.27-1) unstable; urgency=medium + + [ X4lldux ] + * Move/Copy many files in one step via nautilus extension + + [ Marek Marczykowski-Górecki ] + * centos: exclude only dconf user profile, keep dpi config + * travis: add centos7 + * Fix packaging: 'user' group, BACKEND_VMM var + * Create /etc/dconf/profile/user dynamically, if not present + * Require dconf utility to (re)build /etc/dconf/db/local + * Fix make clean + * qubes-firewall: reject packets instead of dropping + + -- Marek Marczykowski-Górecki Wed, 02 May 2018 05:05:33 +0200 + +qubes-core-agent (4.0.26-1) unstable; urgency=medium + + * Change repository URLs to https + + -- Marek Marczykowski-Górecki Sun, 22 Apr 2018 00:29:02 +0200 + +qubes-core-agent (4.0.25-1) unstable; urgency=medium + + [ Marek Marczykowski-Górecki ] + * debian: don't call dconf if it isn't installed + * qrexec: add qrexec-client-vm --buffer-size option + + [ unman ] + * Add missing services in Ubuntu templates. + + [ Simon Gaiser ] + * qrexec-fork-server: Always initialize addrlen argument of accept() + + [ Marek Marczykowski-Górecki ] + * qrexec: fix arguments handling + * Move 'qubesxdg' into qubesagent python package + * Fix shell calls in Makefile + * Fix waiting for application exit in qubesagent.xdg.launch + * Load only test_* files when looking for tests (python) + * qubes-session-autostart: do not wait for applications exit + * Do not start dkms.service + * network: do not assume IPv6 gateway is a link-local address + * qubes-firewall: handle only traffic originating from VMs + * network: make sure static NM configuration is created before NM + start + + [ Davíð Steinn Geirsson ] + * Add misc/qubes-run-terminal to launch any available terminal + emulator + + [ Frédéric Pierret ] + * Create .spec.in and Source0 + * Remove _builddir + * spec.in: add changelog placeholder + * spec.in: fix %if expressions and remove useless conditions + + [ Vladimir Lushnikov ] + * Problem: Unable to use pkg.install with Salt in dom0 when using + UpdateVM that has only yum due to incorrect options passed by Salt + assuming dnf presence + + [ Marek Marczykowski-Górecki ] + * Use only /etc/skel to provision user's home directory of new VM + * Update gitignore and make clean target + * qubes-firewall: signal service readiness only after initial scripts + + [ Frédéric Pierret ] + * Fix GCC8 warnings + * Add missing python-setuptools dependency + * Use %{python3_pkgversion} instead of duplicating python3 targets + + [ Marek Marczykowski-Górecki ] + * travis: update Fedora versions + + -- Marek Marczykowski-Górecki Sat, 21 Apr 2018 15:10:20 +0200 + +qubes-core-agent (4.0.24-1) unstable; urgency=medium + + [ Marek Marczykowski-Górecki ] + * Drop Fedora < 22 support + * Call qubes.PostInstall service to notify dom0 about all + apps/features + * dom0-updates: refactor for ease adding new actions with old yum + * dom0-update: add some approximation of 'list', 'search' and + 'reinstall' + * Drop fakeroot for list/search actions on Debian + + [ Rusty Bird ] + * Really enable qubes-sync-time.timer + + [ Frédéric Pierret ] + * centos: fix conflict with dconf + + [ Marek Marczykowski-Górecki ] + * Speed up initial /rw setup + + [ awokd ] + * reinstal -> reinstall + + -- Marek Marczykowski-Górecki Tue, 27 Feb 2018 15:17:51 +0100 + qubes-core-agent (4.0.23-1) unstable; urgency=medium * qrexec: launch services in login shell diff --git a/debian/control b/debian/control index aa58554..72dc116 100644 --- a/debian/control +++ b/debian/control @@ -26,6 +26,7 @@ Vcs-Git: https://github.com/QubesOS/qubes-core-agent-linux Package: qubes-core-agent Architecture: any Depends: + dconf-cli, dmsetup, gawk, imagemagick, @@ -45,6 +46,7 @@ Depends: python-dbus, qubes-utils (>= 3.1.3), qubes-core-agent-qrexec, + qubesdb-vm, systemd, x11-xserver-utils, xdg-user-dirs, diff --git a/debian/qubes-core-agent.install b/debian/qubes-core-agent.install index 71ed3bc..226c051 100644 --- a/debian/qubes-core-agent.install +++ b/debian/qubes-core-agent.install @@ -3,7 +3,6 @@ etc/apt/apt.conf.d/00notify-hook etc/apt/apt.conf.d/70no-unattended etc/apt/sources.list.d/qubes-r4.list etc/apt/trusted.gpg.d/qubes-archive-keyring.gpg -etc/dconf/profile/user etc/dconf/db/local.d/dpi etc/default/grub.d/30-qubes.cfg etc/fstab @@ -92,6 +91,7 @@ lib/systemd/system/systemd-timesyncd.service.d/30_qubes.conf usr/bin/qubes-desktop-run usr/bin/qubes-open usr/bin/qubes-session-autostart +usr/bin/qubes-run-terminal usr/bin/qvm-copy usr/bin/qvm-copy-to-vm usr/bin/qvm-features-request @@ -102,7 +102,6 @@ usr/bin/qvm-open-in-vm usr/bin/qvm-run-vm usr/bin/qvm-sync-clock usr/bin/xenstore-watch-qubes -usr/lib/python2.7/dist-packages/qubesxdg.py usr/lib/python2.7/dist-packages/qubesagent-*.egg-info/* usr/lib/python2.7/dist-packages/qubesagent/* usr/lib/qubes-bind-dirs.d/30_cron.conf @@ -142,6 +141,7 @@ usr/lib/systemd/user/pulseaudio.socket.d/30_qubes.conf usr/share/glib-2.0/schemas/* usr/share/kde4/services/*.desktop usr/share/kservices5/ServiceMenus/*.desktop +usr/share/applications/*.desktop usr/share/man/man1/qvm-* usr/share/qubes/mime-override/globs usr/share/qubes/qubes-master-key.asc diff --git a/debian/qubes-core-agent.postinst b/debian/qubes-core-agent.postinst index f06b9ba..a3baf49 100755 --- a/debian/qubes-core-agent.postinst +++ b/debian/qubes-core-agent.postinst @@ -145,6 +145,8 @@ case "${1}" in fi systemctl reenable haveged + chgrp user /var/lib/qubes/dom0-updates + debug "UPDATE..." # disable some Upstart services for init in plymouth-shutdown \ @@ -179,7 +181,15 @@ case "${1}" in glib-compile-schemas /usr/share/glib-2.0/schemas || true - dconf update || true + if ! [ -r /etc/dconf/profile/user ]; then + mkdir -p /etc/dconf/profile + echo "user-db:user" >> /etc/dconf/profile/user + echo "system-db:local" >> /etc/dconf/profile/user + fi + + if [ -x /usr/bin/dconf ]; then + dconf update + fi # tell dom0 about installed updates (applications, features etc) /etc/qubes-rpc/qubes.PostInstall || true diff --git a/doc/vm-tools/qrexec-client-vm.rst b/doc/vm-tools/qrexec-client-vm.rst index 4cefa9d..342f517 100644 --- a/doc/vm-tools/qrexec-client-vm.rst +++ b/doc/vm-tools/qrexec-client-vm.rst @@ -8,7 +8,7 @@ qrexec-client-vm - call Qubes RPC service SYNOPSIS ======== -| qrexec-client-vm *target_vmname* *service* [*local_program* [*local program arguments*]] +| qrexec-client-vm [--buffer-size=*BUFFER_SIZE*] *target_vmname* *service* [*local_program* [*local program arguments*]] DESCRIPTION =========== @@ -27,6 +27,12 @@ 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 diff --git a/init/functions b/init/functions index 38aee57..82f4a5b 100644 --- a/init/functions +++ b/init/functions @@ -158,15 +158,9 @@ initialize_home() { homedir=$(echo "$pair" | awk -F : ' { print $4 } ') homedirwithouthome=${homedir#/home/} if ! test -d "$home_root/$homedirwithouthome" || [ "$mode" = "unconditionally" ] ; then - if [ "$homedir" == "/home/user" ] && [ -d "/home.orig/$homedirwithouthome" ] ; then - echo "initialize_home: populating $mode $home_root/$homedirwithouthome from /home.orig/$homedirwithouthome" >&2 - mkdir -p "$home_root/$homedirwithouthome" - cp -af -T "/home.orig/$homedirwithouthome" "$home_root/$homedirwithouthome" - else - echo "initialize_home: populating $mode $home_root/$homedirwithouthome from /etc/skel" >&2 - mkdir -p "$home_root/$homedirwithouthome" - cp -af -T /etc/skel "$home_root/$homedirwithouthome" - fi + echo "initialize_home: populating $mode $home_root/$homedirwithouthome from /etc/skel" >&2 + mkdir -p "$home_root/$homedirwithouthome" + cp -af -T /etc/skel "$home_root/$homedirwithouthome" echo "initialize_home: adjusting permissions $mode on $home_root/$homedirwithouthome" >&2 chown -R "$uid" "$home_root/$homedirwithouthome" & waitpids="$!" diff --git a/misc/Makefile b/misc/Makefile index 01533c3..5001802 100644 --- a/misc/Makefile +++ b/misc/Makefile @@ -23,7 +23,7 @@ python2: python3: rm -rf py3 mkdir -p py3 - cp dnf-qubes-hooks.py qubesxdg.py py3/ + cp dnf-qubes-hooks.py py3/ python3 -m compileall py3 python3 -O -m compileall py3 diff --git a/misc/qubes-desktop-run b/misc/qubes-desktop-run index e1116fb..89df349 100755 --- a/misc/qubes-desktop-run +++ b/misc/qubes-desktop-run @@ -1,6 +1,6 @@ #!/usr/bin/python -from qubesxdg import launch +from qubesagent.xdg import launch import sys if __name__ == '__main__': diff --git a/misc/qubes-download-dom0-updates.sh b/misc/qubes-download-dom0-updates.sh index b720715..4abe8b2 100755 --- a/misc/qubes-download-dom0-updates.sh +++ b/misc/qubes-download-dom0-updates.sh @@ -53,6 +53,9 @@ fi YUM="yum" if type dnf >/dev/null 2>&1; then YUM="dnf --best --allowerasing --noplugins" +else + # salt in dom0 thinks it's using dnf but we only have yum so need to remove extra options + OPTS="${OPTS/--best --allowerasing/}" fi if ! [ -d "$DOM0_UPDATES_DIR" ]; then @@ -126,7 +129,7 @@ if ! $YUM --help | grep -q downloadonly; then elif [ "$YUM_ACTION" == "list" ] || [ "$YUM_ACTION" == "search" ]; then # those actions do not download any package, so lack of --downloadonly is irrelevant YUM_COMMAND="$YUM $YUM_ACTION -y" - elif [ "$YUM_ACTION" == "reinstal" ]; then + elif [ "$YUM_ACTION" == "reinstall" ]; then # this is just approximation of 'reinstall' action... # shellcheck disable=SC2086 PKGLIST=$(rpm --root=$DOM0_UPDATES_DIR -q $PKGLIST) diff --git a/misc/qubes-r4.repo b/misc/qubes-r4.repo index 92aa78f..20f9606 100644 --- a/misc/qubes-r4.repo +++ b/misc/qubes-r4.repo @@ -1,6 +1,6 @@ [qubes-vm-r4.0-current] name = Qubes OS Repository for VM (updates) -baseurl = http://yum.qubes-os.org/r4.0/current/vm/fc$releasever +baseurl = https://yum.qubes-os.org/r4.0/current/vm/fc$releasever gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-qubes-4-primary skip_if_unavailable=False gpgcheck = 1 @@ -8,7 +8,7 @@ enabled=1 [qubes-vm-r4.0-current-testing] name = Qubes OS Repository for VM (updates-testing) -baseurl = http://yum.qubes-os.org/r4.0/current-testing/vm/fc$releasever +baseurl = https://yum.qubes-os.org/r4.0/current-testing/vm/fc$releasever gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-qubes-4-primary skip_if_unavailable=False gpgcheck = 1 @@ -16,7 +16,7 @@ enabled=0 [qubes-vm-r4.0-security-testing] name = Qubes OS Repository for VM (security-testing) -baseurl = http://yum.qubes-os.org/r4.0/security-testing/vm/fc$releasever +baseurl = https://yum.qubes-os.org/r4.0/security-testing/vm/fc$releasever gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-qubes-4-primary skip_if_unavailable=False gpgcheck = 1 @@ -24,7 +24,7 @@ enabled=0 [qubes-vm-r4.0-unstable] name = Qubes OS Repository for VM (unstable) -baseurl = http://yum.qubes-os.org/r4.0/unstable/vm/fc$releasever +baseurl = https://yum.qubes-os.org/r4.0/unstable/vm/fc$releasever gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-qubes-4-unstable gpgcheck = 1 enabled=0 diff --git a/misc/qubes-run-terminal b/misc/qubes-run-terminal new file mode 100755 index 0000000..73b705d --- /dev/null +++ b/misc/qubes-run-terminal @@ -0,0 +1,11 @@ +#!/bin/sh +# Try to find a terminal emulator that's installed and run it. + +for terminal in x-terminal-emulator gnome-terminal xfce4-terminal konsole urxvt rxvt termit terminator Eterm aterm roxterm termite lxterminal mate-terminal terminology st xterm; do + if which $terminal >/dev/null 2>&1 ; then + exec "$terminal" + fi +done + +echo "ERROR: No suitable terminal found." > /dev/stderr + diff --git a/misc/qubes-run-terminal.desktop b/misc/qubes-run-terminal.desktop new file mode 100644 index 0000000..7bb1d8f --- /dev/null +++ b/misc/qubes-run-terminal.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Name=Run Terminal +Exec=qubes-run-terminal +Icon=utilities-terminal +Type=Application diff --git a/misc/qubes-session-autostart b/misc/qubes-session-autostart index e7bfe11..04a8de3 100644 --- a/misc/qubes-session-autostart +++ b/misc/qubes-session-autostart @@ -25,7 +25,7 @@ import subprocess import sys from xdg.DesktopEntry import DesktopEntry -from qubesxdg import launch +from qubesagent.xdg import launch import xdg.BaseDirectory import os @@ -76,7 +76,7 @@ def process_autostart(environments): else: entry = DesktopEntry(entry_path) if entry_should_be_started(entry, environments): - launch(entry_path) + launch(entry_path, wait=False) except Exception as e: print >>sys.stderr, "Failed to process '{}': {}".format( entry_name, str(e) diff --git a/misc/uca_qubes.xml b/misc/uca_qubes.xml index 2e4ad8d..64a3ce9 100644 --- a/misc/uca_qubes.xml +++ b/misc/uca_qubes.xml @@ -59,7 +59,7 @@ document-open - Open in DisposableVM + Edit in DisposableVM 1507455559234996-8 /usr/lib/qubes/qvm-actions.sh opendvm %F @@ -70,3 +70,16 @@ + + document-open + View in DisposableVM + 1507455559234997-9 + /usr/lib/qubes/qvm-actions.sh viewdvm %F + + * + + + + + + diff --git a/network/network-manager-prepare-conf-dir b/network/network-manager-prepare-conf-dir index d56b996..7eb09c1 100755 --- a/network/network-manager-prepare-conf-dir +++ b/network/network-manager-prepare-conf-dir @@ -16,4 +16,15 @@ unmanaged_devices=mac:fe:ff:ff:ff:ff:ff sed -r -i -e "s/^#?unmanaged-devices=.*/unmanaged-devices=$unmanaged_devices/" /etc/NetworkManager/NetworkManager.conf sed -r -i -e "s/^#?plugins=.*/plugins=keyfile/" /etc/NetworkManager/NetworkManager.conf +# setup uplink configuration if applicable - this needs to be done before +# starting NetworkManager, otherwise it will try default DHCP configuration +# first and only after a timeout fallback to static one - introducing delay in +# network connectivity +export INTERFACE=eth0 +if qubesdb-read /qubes-ip >/dev/null 2>/dev/null && + [ -e /sys/class/net/$INTERFACE ] && + [ ! -r /etc/NetworkManager/system-connections/qubes-uplink-$INTERFACE ]; then + /usr/lib/qubes/setup-ip +fi + exit 0 diff --git a/network/qubes-iptables b/network/qubes-iptables index 5688bff..6daa9ac 100755 --- a/network/qubes-iptables +++ b/network/qubes-iptables @@ -42,9 +42,15 @@ start() { # Do not start if there is no config file. [ ! -f "$IPTABLES_DATA" ] && return 6 + CMD_ARGS= + if "$CMD-restore" --help 2>&1 | grep -q wait=; then + # 'wait' must be last on command line if secs not specified + CMD_ARGS=--wait + fi + echo -n $"${CMD}: Applying firewall rules: " - "$CMD-restore" "$IPTABLES_DATA" + "$CMD-restore" "$IPTABLES_DATA" $CMD_ARGS ret="$?" if [ "$ret" -eq 0 ]; then echo OK diff --git a/network/setup-ip b/network/setup-ip index 7bc8375..04b4d7b 100755 --- a/network/setup-ip +++ b/network/setup-ip @@ -91,6 +91,9 @@ __EOF__ fi /sbin/ifconfig "$INTERFACE" up /sbin/route add -host "$gateway" dev "$INTERFACE" + if [ -n "$gateway6" ] && ! echo "$gateway6" | grep -q "^fe80:"; then + /sbin/route -6 add "$gateway6/128" dev "$INTERFACE" + fi if ! qsvc disable-default-route ; then /sbin/route add default gw "$gateway" if [ -n "$gateway6" ]; then diff --git a/network/vif-route-qubes b/network/vif-route-qubes index 07506b2..78a7c10 100755 --- a/network/vif-route-qubes +++ b/network/vif-route-qubes @@ -40,10 +40,12 @@ if [ "${ip}" ]; then # IPs as seen by this VM netvm_ip="$ip4" netvm_gw_ip=$(qubesdb-read /qubes-netvm-gateway) + netvm_gw_ip6=$(qubesdb-read /qubes-netvm-gateway6 || :) netvm_dns1_ip=$(qubesdb-read /qubes-netvm-primary-dns) netvm_dns2_ip=$(qubesdb-read /qubes-netvm-secondary-dns) back_ip="$netvm_gw_ip" + back_ip6="$netvm_gw_ip6" # IPs as seen by the VM - if other than $netvm_ip appvm_gw_ip="$(qubesdb-read "/mapped-ip/$ip4/visible-gateway" 2>/dev/null || :)" @@ -106,7 +108,10 @@ if [ "${ip}" ] ; then echo -e "*raw\n$iptables_cmd -i ${vif} -j DROP\nCOMMIT" | \ ${cmdprefix} flock $lockfile ip6tables-restore --noflush fi - ${cmdprefix} ip addr "${ipcmd}" "${back_ip}/32" dev "${vif}" + ${cmdprefix} ip addr "${ipcmd}" "${back_ip}/32" dev "${vif}" + if [ "${back_ip6}" ] && [[ "${back_ip6}" != "fe80:"* ]]; then + ${cmdprefix} ip addr "${ipcmd}" "${back_ip6}/128" dev "${vif}" + fi fi log debug "Successful vif-route-qubes $command for $vif." diff --git a/qrexec/qrexec-agent-data.c b/qrexec/qrexec-agent-data.c index 147670c..7d314f9 100644 --- a/qrexec/qrexec-agent-data.c +++ b/qrexec/qrexec-agent-data.c @@ -295,6 +295,7 @@ int process_child_io(libvchan_t *data_vchan, 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); @@ -386,7 +387,7 @@ int process_child_io(libvchan_t *data_vchan, /* 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, NULL, &selectmask); + ret = pselect(max_fd + 1, &rdset, &wrset, NULL, &normal_timeout, &selectmask); if (ret < 0) { if (errno == EINTR) continue; @@ -490,10 +491,14 @@ int process_child_io(libvchan_t *data_vchan, * 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 stdin_fd, int stdout_fd, int stderr_fd /* MSG_SERVICE_CONNECT */, + int buffer_size) { libvchan_t *data_vchan; int exit_code = 0; @@ -504,9 +509,12 @@ int handle_new_process_common(int type, int connect_domain, int connect_port, 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, - VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE); + buffer_size, buffer_size); if (data_vchan) libvchan_wait(data_vchan); } else { @@ -563,7 +571,7 @@ pid_t handle_new_process(int type, int connect_domain, int connect_port, /* child process */ exit_code = handle_new_process_common(type, connect_domain, connect_port, cmdline, cmdline_len, - -1, -1, -1); + -1, -1, -1, 0); exit(exit_code); /* suppress warning */ @@ -572,13 +580,13 @@ pid_t handle_new_process(int type, int connect_domain, int connect_port, /* 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 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); + NULL, 0, stdin_fd, stdout_fd, stderr_fd, buffer_size); return exit_code; } diff --git a/qrexec/qrexec-agent.c b/qrexec/qrexec-agent.c index 46e5566..f9b4ff7 100644 --- a/qrexec/qrexec-agent.c +++ b/qrexec/qrexec-agent.c @@ -382,7 +382,7 @@ int try_fork_server(int type, int connect_domain, int connect_port, remote.sun_family = AF_UNIX; strncpy(remote.sun_path, fork_server_socket_path, - sizeof(remote.sun_path)); + sizeof(remote.sun_path) - 1); free(fork_server_socket_path); if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { diff --git a/qrexec/qrexec-agent.h b/qrexec/qrexec-agent.h index 05b86c1..7cd11a2 100644 --- a/qrexec/qrexec-agent.h +++ b/qrexec/qrexec-agent.h @@ -37,7 +37,8 @@ pid_t handle_new_process(int type, 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 stdin_fd, int stdout_fd, int stderr_fd, + int buffer_size); struct qrexec_cmd_info { diff --git a/qrexec/qrexec-client-vm.c b/qrexec/qrexec-client-vm.c index b5bd86d..0065d00 100644 --- a/qrexec/qrexec-client-vm.c +++ b/qrexec/qrexec-client-vm.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "libqrexec-utils.h" #include "qrexec.h" #include "qrexec-agent.h" @@ -54,7 +55,7 @@ int connect_unix_socket(char *path) remote.sun_family = AF_UNIX; strncpy(remote.sun_path, path, - sizeof(remote.sun_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"); @@ -85,6 +86,19 @@ void convert_target_name_keyword(char *target) 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; @@ -95,25 +109,37 @@ int main(int argc, char **argv) char *abs_exec_path; pid_t child_pid = 0; int inpipe[2], outpipe[2]; + int buffer_size = 0; + int opt; - if (argc < 3) { - fprintf(stderr, - "usage: %s target_vmname program_ident [local_program [local program arguments]]\n", - argv[0]); - exit(1); + 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 > 3) { + + 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[2], sizeof(params.service_name)); + strncpy(params.service_name, argv[optind + 1], sizeof(params.service_name) - 1); - convert_target_name_keyword(argv[1]); - strncpy(params.target_domain, argv[1], - sizeof(params.target_domain)); + 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"); @@ -164,9 +190,9 @@ int main(int argc, char **argv) close(inpipe[0]); close(outpipe[1]); - abs_exec_path = strdup(argv[3]); - argv[3] = get_program_name(argv[3]); - execv(abs_exec_path, argv + 3); + 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); } @@ -175,11 +201,11 @@ int main(int argc, char **argv) ret = handle_data_client(MSG_SERVICE_CONNECT, exec_params.connect_domain, exec_params.connect_port, - inpipe[1], outpipe[0], -1); + 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); + 1, 0, -1, buffer_size); } close(trigger_fd); diff --git a/qrexec/qrexec-fork-server.c b/qrexec/qrexec-fork-server.c index 43bfd54..6c5eb98 100644 --- a/qrexec/qrexec-fork-server.c +++ b/qrexec/qrexec-fork-server.c @@ -112,12 +112,15 @@ int main(int argc, char **argv) { signal(SIGCHLD, SIG_IGN); register_exec_func(do_exec); - while ((fd = accept(s, (struct sockaddr *) &peer, &addrlen)) >= 0) { + 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); - addrlen = sizeof(peer); } close(s); unlink(socket_path); diff --git a/qubes-rpc/.gitignore b/qubes-rpc/.gitignore index 283f264..5fc44d8 100644 --- a/qubes-rpc/.gitignore +++ b/qubes-rpc/.gitignore @@ -1,6 +1,5 @@ qubes_add_pendrive_script qubes_penctl -qvm-open-in-dvm dvm_file_editor qfile-agent qfile-agent-dvm diff --git a/qubes-rpc/dvm2.h b/qubes-rpc/dvm2.h index 0e5922c..72b73f7 100644 --- a/qubes-rpc/dvm2.h +++ b/qubes-rpc/dvm2.h @@ -1,2 +1,3 @@ #define DVM_FILENAME_SIZE 256 #define DVM_SPOOL "/home/user/.dvmspool" +#define DVM_VIEW_ONLY_PREFIX "view-only-" diff --git a/qubes-rpc/gui-fatal.c b/qubes-rpc/gui-fatal.c index 5900ce5..d50db09 100644 --- a/qubes-rpc/gui-fatal.c +++ b/qubes-rpc/gui-fatal.c @@ -10,58 +10,58 @@ static void fix_display(void) { - setenv("DISPLAY", ":0", 1); + setenv("DISPLAY", ":0", 1); } static void produce_message(const char * type, const char *fmt, va_list args) { - char *dialog_msg; - char buf[1024]; - (void) vsnprintf(buf, sizeof(buf), fmt, args); - if (asprintf(&dialog_msg, "%s: %s: %s (error type: %s)", - program_invocation_short_name, type, buf, strerror(errno)) < 0) { - fprintf(stderr, "Failed to allocate memory for error message :(\n"); - return; - } - fprintf(stderr, "%s\n", dialog_msg); - switch (fork()) { - case -1: - exit(1); //what else - case 0: - if (geteuid() == 0) - if (setuid(getuid()) != 0) - perror("setuid failed, calling kdialog/zenity as root"); - fix_display(); + char *dialog_msg; + char buf[1024]; + (void) vsnprintf(buf, sizeof(buf), fmt, args); + if (asprintf(&dialog_msg, "%s: %s: %s (error type: %s)", + program_invocation_short_name, type, buf, strerror(errno)) < 0) { + fprintf(stderr, "Failed to allocate memory for error message :(\n"); + return; + } + fprintf(stderr, "%s\n", dialog_msg); + switch (fork()) { + case -1: + exit(1); //what else + case 0: + if (geteuid() == 0) + if (setuid(getuid()) != 0) + perror("setuid failed, calling kdialog/zenity as root"); + fix_display(); #ifdef USE_KDIALOG - execlp("/usr/bin/kdialog", "kdialog", "--sorry", dialog_msg, NULL); + execlp("/usr/bin/kdialog", "kdialog", "--sorry", dialog_msg, NULL); #else - execlp("/usr/bin/zenity", "zenity", "--error", "--text", dialog_msg, NULL); + execlp("/usr/bin/zenity", "zenity", "--error", "--text", dialog_msg, NULL); #endif - exit(1); - default:; - } - free(dialog_msg); + exit(1); + default:; + } + free(dialog_msg); } void gui_fatal(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - produce_message("Fatal error", fmt, args); - va_end(args); - exit(1); + va_list args; + va_start(args, fmt); + produce_message("Fatal error", fmt, args); + va_end(args); + exit(1); } void qfile_gui_fatal(const char *fmt, va_list args) { - produce_message("Fatal error", fmt, args); + produce_message("Fatal error", fmt, args); exit(1); } void gui_nonfatal(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - produce_message("Information", fmt, args); - va_end(args); + va_list args; + va_start(args, fmt); + produce_message("Information", fmt, args); + va_end(args); } diff --git a/qubes-rpc/qfile-agent.c b/qubes-rpc/qfile-agent.c index de65788..42a1dfd 100644 --- a/qubes-rpc/qfile-agent.c +++ b/qubes-rpc/qfile-agent.c @@ -13,107 +13,107 @@ #include enum { - PROGRESS_FLAG_NORMAL, - PROGRESS_FLAG_INIT, - PROGRESS_FLAG_DONE + PROGRESS_FLAG_NORMAL, + PROGRESS_FLAG_INIT, + PROGRESS_FLAG_DONE }; void do_notify_progress(long long total, int flag) { - const char *du_size_env = getenv("FILECOPY_TOTAL_SIZE"); - const char *progress_type_env = getenv("PROGRESS_TYPE"); - const char *saved_stdout_env = getenv("SAVED_FD_1"); - int ignore; - if (!progress_type_env) - return; - if (!strcmp(progress_type_env, "console") && du_size_env) { - char msg[256]; - snprintf(msg, sizeof(msg), "sent %lld/%lld KB\r", - total / 1024, strtoull(du_size_env, NULL, 0)); - ignore = write(2, msg, strlen(msg)); - if (flag == PROGRESS_FLAG_DONE) - ignore = write(2, "\n", 1); - } - if (!strcmp(progress_type_env, "gui") && saved_stdout_env) { - char msg[256]; - snprintf(msg, sizeof(msg), "%lld\n", total); - ignore = write(strtoul(saved_stdout_env, NULL, 0), msg, - strlen(msg)); - } - if (ignore < 0) { - /* silence gcc warning */ - } + const char *du_size_env = getenv("FILECOPY_TOTAL_SIZE"); + const char *progress_type_env = getenv("PROGRESS_TYPE"); + const char *saved_stdout_env = getenv("SAVED_FD_1"); + int ignore; + if (!progress_type_env) + return; + if (!strcmp(progress_type_env, "console") && du_size_env) { + char msg[256]; + snprintf(msg, sizeof(msg), "sent %lld/%lld KB\r", + total / 1024, strtoull(du_size_env, NULL, 0)); + ignore = write(2, msg, strlen(msg)); + if (flag == PROGRESS_FLAG_DONE) + ignore = write(2, "\n", 1); + } + if (!strcmp(progress_type_env, "gui") && saved_stdout_env) { + char msg[256]; + snprintf(msg, sizeof(msg), "%lld\n", total); + ignore = write(strtoul(saved_stdout_env, NULL, 0), msg, + strlen(msg)); + } + if (ignore < 0) { + /* silence gcc warning */ + } } void notify_progress(int size, int flag) { - static long long total = 0; - static long long prev_total = 0; - total += size; - if (total > prev_total + PROGRESS_NOTIFY_DELTA - || (flag != PROGRESS_FLAG_NORMAL)) { - // check for possible error from qfile-unpacker; if error occured, - // exit() will be called, so don't bother with current state - // (notify_progress can be called as callback from copy_file()) - if (flag == PROGRESS_FLAG_NORMAL) - wait_for_result(); - do_notify_progress(total, flag); - prev_total = total; - } + static long long total = 0; + static long long prev_total = 0; + total += size; + if (total > prev_total + PROGRESS_NOTIFY_DELTA + || (flag != PROGRESS_FLAG_NORMAL)) { + // check for possible error from qfile-unpacker; if error occured, + // exit() will be called, so don't bother with current state + // (notify_progress can be called as callback from copy_file()) + if (flag == PROGRESS_FLAG_NORMAL) + wait_for_result(); + do_notify_progress(total, flag); + prev_total = total; + } } char *get_abs_path(const char *cwd, const char *pathname) { - char *ret; - if (pathname[0] == '/') - return strdup(pathname); - if (asprintf(&ret, "%s/%s", cwd, pathname) < 0) - return NULL; - else - return ret; + char *ret; + if (pathname[0] == '/') + return strdup(pathname); + if (asprintf(&ret, "%s/%s", cwd, pathname) < 0) + return NULL; + else + return ret; } int main(int argc, char **argv) { - int i; - char *entry; - char *cwd; - char *sep; - int ignore_symlinks = 0; + int i; + char *entry; + char *cwd; + char *sep; + int ignore_symlinks = 0; - qfile_pack_init(); - register_error_handler(qfile_gui_fatal); - register_notify_progress(¬ify_progress); - notify_progress(0, PROGRESS_FLAG_INIT); - cwd = getcwd(NULL, 0); - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "--ignore-symlinks")==0) { - ignore_symlinks = 1; - continue; - } + qfile_pack_init(); + register_error_handler(qfile_gui_fatal); + register_notify_progress(¬ify_progress); + notify_progress(0, PROGRESS_FLAG_INIT); + cwd = getcwd(NULL, 0); + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "--ignore-symlinks")==0) { + ignore_symlinks = 1; + continue; + } - entry = get_abs_path(cwd, argv[i]); + entry = get_abs_path(cwd, argv[i]); - do { - sep = rindex(entry, '/'); - if (!sep) - gui_fatal - ("Internal error: nonabsolute filenames not allowed"); - *sep = 0; - } while (sep[1] == 0); - if (entry[0] == 0) { - if (chdir("/") < 0) { - gui_fatal("Internal error: chdir(\"/\") failed?!"); - } - } else if (chdir(entry)) - gui_fatal("chdir to %s", entry); - do_fs_walk(sep + 1, ignore_symlinks); - free(entry); - } - notify_end_and_wait_for_result(); - notify_progress(0, PROGRESS_FLAG_DONE); - return 0; + do { + sep = rindex(entry, '/'); + if (!sep) + gui_fatal + ("Internal error: nonabsolute filenames not allowed"); + *sep = 0; + } while (sep[1] == 0); + if (entry[0] == 0) { + if (chdir("/") < 0) { + gui_fatal("Internal error: chdir(\"/\") failed?!"); + } + } else if (chdir(entry)) + gui_fatal("chdir to %s", entry); + do_fs_walk(sep + 1, ignore_symlinks); + free(entry); + } + notify_end_and_wait_for_result(); + notify_progress(0, PROGRESS_FLAG_DONE); + return 0; } diff --git a/qubes-rpc/qfile-unpacker.c b/qubes-rpc/qfile-unpacker.c index 86a9527..817277a 100644 --- a/qubes-rpc/qfile-unpacker.c +++ b/qubes-rpc/qfile-unpacker.c @@ -17,81 +17,81 @@ #define INCOMING_DIR_ROOT "/home/user/QubesIncoming" int prepare_creds_return_uid(const char *username) { - const struct passwd *pwd; - pwd = getpwnam(username); - if (!pwd) { - perror("getpwnam"); - exit(1); - } - setenv("HOME", pwd->pw_dir, 1); - setenv("USER", username, 1); - if (setgid(pwd->pw_gid) < 0) - gui_fatal("Error setting group permissions"); - if (initgroups(username, pwd->pw_gid) < 0) - gui_fatal("Error initializing groups"); - if (setfsuid(pwd->pw_uid) < 0) - gui_fatal("Error setting filesystem level permissions"); - return pwd->pw_uid; + const struct passwd *pwd; + pwd = getpwnam(username); + if (!pwd) { + perror("getpwnam"); + exit(1); + } + setenv("HOME", pwd->pw_dir, 1); + setenv("USER", username, 1); + if (setgid(pwd->pw_gid) < 0) + gui_fatal("Error setting group permissions"); + if (initgroups(username, pwd->pw_gid) < 0) + gui_fatal("Error initializing groups"); + if (setfsuid(pwd->pw_uid) < 0) + gui_fatal("Error setting filesystem level permissions"); + return pwd->pw_uid; } int main(int argc __attribute((__unused__)), char ** argv __attribute__((__unused__))) { - char *incoming_dir; - int uid, ret; - pid_t pid; - const char *remote_domain; - char *procdir_path; - int procfs_fd; + char *incoming_dir; + int uid, ret; + pid_t pid; + const char *remote_domain; + char *procdir_path; + int procfs_fd; - uid = prepare_creds_return_uid("user"); + uid = prepare_creds_return_uid("user"); - remote_domain = getenv("QREXEC_REMOTE_DOMAIN"); - if (!remote_domain) { - gui_fatal("Cannot get remote domain name"); - exit(1); - } - mkdir(INCOMING_DIR_ROOT, 0700); - if (asprintf(&incoming_dir, "%s/%s", INCOMING_DIR_ROOT, remote_domain) < 0) - gui_fatal("Error allocating memory"); - mkdir(incoming_dir, 0700); - if (chdir(incoming_dir)) - gui_fatal("Error chdir to %s", incoming_dir); + remote_domain = getenv("QREXEC_REMOTE_DOMAIN"); + if (!remote_domain) { + gui_fatal("Cannot get remote domain name"); + exit(1); + } + mkdir(INCOMING_DIR_ROOT, 0700); + if (asprintf(&incoming_dir, "%s/%s", INCOMING_DIR_ROOT, remote_domain) < 0) + gui_fatal("Error allocating memory"); + mkdir(incoming_dir, 0700); + if (chdir(incoming_dir)) + gui_fatal("Error chdir to %s", incoming_dir); - if (mount(".", ".", NULL, MS_BIND | MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0) - gui_fatal("Failed to mount a directory %s", incoming_dir); + if (mount(".", ".", NULL, MS_BIND | MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0) + gui_fatal("Failed to mount a directory %s", incoming_dir); - /* parse the input in unprivileged child process, parent will hold root - * access to unmount incoming dir */ - switch (pid=fork()) { - case -1: - gui_fatal("Failed to create new process"); - case 0: - if (asprintf(&procdir_path, "/proc/%d/fd", getpid()) < 0) { - gui_fatal("Error allocating memory"); - } - procfs_fd = open(procdir_path, O_DIRECTORY | O_RDONLY); - if (procfs_fd < 0) - perror("Failed to open /proc"); - else - set_procfs_fd(procfs_fd); - free(procdir_path); + /* parse the input in unprivileged child process, parent will hold root + * access to unmount incoming dir */ + switch (pid=fork()) { + case -1: + gui_fatal("Failed to create new process"); + case 0: + if (asprintf(&procdir_path, "/proc/%d/fd", getpid()) < 0) { + gui_fatal("Error allocating memory"); + } + procfs_fd = open(procdir_path, O_DIRECTORY | O_RDONLY); + if (procfs_fd < 0) + perror("Failed to open /proc"); + else + set_procfs_fd(procfs_fd); + free(procdir_path); - if (chroot(".")) - gui_fatal("Error chroot to %s", incoming_dir); - if (setuid(uid) < 0) { - /* no kdialog inside chroot */ - perror("setuid"); - exit(1); - } - return do_unpack(); - } - if (waitpid(pid, &ret, 0) < 0) { - gui_fatal("Failed to wait for child process"); - } - if (umount2(".", MNT_DETACH) < 0) - gui_fatal("Cannot umount incoming directory"); - if (!WIFEXITED(ret)) { - gui_fatal("Child process exited abnormally"); - } - return WEXITSTATUS(ret); + if (chroot(".")) + gui_fatal("Error chroot to %s", incoming_dir); + if (setuid(uid) < 0) { + /* no kdialog inside chroot */ + perror("setuid"); + exit(1); + } + return do_unpack(); + } + if (waitpid(pid, &ret, 0) < 0) { + gui_fatal("Failed to wait for child process"); + } + if (umount2(".", MNT_DETACH) < 0) + gui_fatal("Cannot umount incoming directory"); + if (!WIFEXITED(ret)) { + gui_fatal("Child process exited abnormally"); + } + return WEXITSTATUS(ret); } diff --git a/qubes-rpc/qopen-in-vm.c b/qubes-rpc/qopen-in-vm.c index 4e311b8..62985ff 100644 --- a/qubes-rpc/qopen-in-vm.c +++ b/qubes-rpc/qopen-in-vm.c @@ -9,99 +9,128 @@ #include #include #include +#include #include #include "dvm2.h" -void send_file(const char *fname) +void send_file(const char *fname, int view_only) { - const char *base; - char sendbuf[DVM_FILENAME_SIZE]; - int fd = open(fname, O_RDONLY); - if (fd < 0) - gui_fatal("open %s", fname); - base = rindex(fname, '/'); - if (!base) - base = fname; - else - base++; - if (strlen(base) >= DVM_FILENAME_SIZE) - base += strlen(base) - DVM_FILENAME_SIZE + 1; - strncpy(sendbuf,base,DVM_FILENAME_SIZE); /* fills out with NULs */ - if (!write_all(1, sendbuf, DVM_FILENAME_SIZE)) - gui_fatal("send filename to dispVM"); - if (!copy_fd_all(1, fd)) - gui_fatal("send file to dispVM"); - close(1); - close(fd); + const char *base; + char sendbuf[DVM_FILENAME_SIZE] = {0}; + size_t sendbuf_size = DVM_FILENAME_SIZE; + int fd = open(fname, O_RDONLY); + if (fd < 0) + gui_fatal("open %s", fname); + + _Static_assert(DVM_FILENAME_SIZE > sizeof(DVM_VIEW_ONLY_PREFIX), + "DVM_FILENAME_SIZE > sizeof(DVM_VIEW_ONLY_PREFIX)"); + + if (view_only) { + strncpy(sendbuf, DVM_VIEW_ONLY_PREFIX, sendbuf_size); + sendbuf_size -= strlen(DVM_VIEW_ONLY_PREFIX); + } + base = rindex(fname, '/'); + if (!base) + base = fname; + else + base++; + if (strlen(base) >= sendbuf_size) + base += strlen(base) - sendbuf_size + 1; + strncat(sendbuf,base,sendbuf_size - 1); /* fills out with NULs */ + sendbuf[DVM_FILENAME_SIZE - 1] = '\0'; + if (!write_all(1, sendbuf, DVM_FILENAME_SIZE)) + gui_fatal("send filename to dispVM"); + if (!copy_fd_all(1, fd)) + gui_fatal("send file to dispVM"); + close(1); + close(fd); } int copy_and_return_nonemptiness(int tmpfd) { - struct stat st; - if (!copy_fd_all(tmpfd, 0)) - gui_fatal("receiving file from dispVM"); - if (fstat(tmpfd, &st)) - gui_fatal("fstat"); - close(tmpfd); + struct stat st; + if (!copy_fd_all(tmpfd, 0)) + gui_fatal("receiving file from dispVM"); + if (fstat(tmpfd, &st)) + gui_fatal("fstat"); + close(tmpfd); - return st.st_size > 0; + return st.st_size > 0; } void recv_file_nowrite(const char *fname) { - char *tempfile; - char *errmsg; - int tmpfd = -1; + char *tempfile; + char *errmsg; + int tmpfd = -1; - if (asprintf(&tempfile, "/tmp/file_edited_in_dvm.XXXXXX") != -1) - tmpfd = mkstemp(tempfile); - if (tmpfd < 0) - gui_fatal("unable to create any temporary file, aborting"); - if (!copy_and_return_nonemptiness(tmpfd)) { - unlink(tempfile); - return; - } - if (asprintf(&errmsg, - "The file %s has been edited in Disposable VM and the modified content has been received, " - "but this file is in nonwritable directory and thus cannot be modified safely. The edited file has been " - "saved to %s", fname, tempfile) != -1) + if (asprintf(&tempfile, "/tmp/file_edited_in_dvm.XXXXXX") != -1) + tmpfd = mkstemp(tempfile); + if (tmpfd < 0) + gui_fatal("unable to create any temporary file, aborting"); + if (!copy_and_return_nonemptiness(tmpfd)) { + unlink(tempfile); + return; + } + if (asprintf(&errmsg, + "The file %s has been edited in Disposable VM and the modified content has been received, " + "but this file is in nonwritable directory and thus cannot be modified safely. The edited file has been " + "saved to %s", fname, tempfile) != -1) gui_nonfatal(errmsg); } void actually_recv_file(const char *fname, const char *tempfile, int tmpfd) { - if (!copy_and_return_nonemptiness(tmpfd)) { - unlink(tempfile); - return; - } - if (rename(tempfile, fname)) - gui_fatal("rename"); + if (!copy_and_return_nonemptiness(tmpfd)) { + unlink(tempfile); + return; + } + if (rename(tempfile, fname)) + gui_fatal("rename"); } void recv_file(const char *fname) { - int tmpfd = -1; - char *tempfile; - if (asprintf(&tempfile, "%s.XXXXXX", fname) != -1) { - tmpfd = mkstemp(tempfile); - } - if (tmpfd < 0) - recv_file_nowrite(fname); - else - actually_recv_file(fname, tempfile, tmpfd); -} - -void talk_to_daemon(const char *fname) -{ - send_file(fname); - recv_file(fname); + int tmpfd = -1; + char *tempfile; + if (asprintf(&tempfile, "%s.XXXXXX", fname) != -1) { + tmpfd = mkstemp(tempfile); + } + if (tmpfd < 0) + recv_file_nowrite(fname); + else + actually_recv_file(fname, tempfile, tmpfd); } int main(int argc, char ** argv) { - signal(SIGPIPE, SIG_IGN); - if (argc!=2) - gui_fatal("OpenInVM - no file given?"); - talk_to_daemon(argv[1]); - return 0; + char *fname; + int view_only = 0; + int ret; + const struct option opts[] = { + {"view-only", no_argument, &view_only, 1}, + {0} + }; + + while ((ret=getopt_long(argc, argv, "", opts, NULL)) != -1) { + if (ret == '?') { + exit(2); + } + } + + signal(SIGPIPE, SIG_IGN); + + if (optind >= argc) + gui_fatal("OpenInVM - no file given?"); + fname = argv[optind]; + send_file(fname, view_only); + if (!view_only) { + recv_file(fname); + } else { + /* discard received data */ + int null_fd = open("/dev/null", O_WRONLY); + copy_fd_all(null_fd, 0); + close(null_fd); + } + return 0; } diff --git a/qubes-rpc/qvm-actions.sh b/qubes-rpc/qvm-actions.sh index 691b3f2..3e4a968 100755 --- a/qubes-rpc/qvm-actions.sh +++ b/qubes-rpc/qvm-actions.sh @@ -45,6 +45,12 @@ case "$action" in qvm-open-in-dvm "$file" | zenity --notification --text "Opening $file in DisposableVM..." --timeout 3 & done ;; + viewdvm) + for file in "$@" + do + qvm-open-in-dvm --view-only "$file" | zenity --notification --text "Opening $file in DisposableVM..." --timeout 3 & + done + ;; *) echo "Unknown action. Aborting..." exit 1 diff --git a/qubes-rpc/qvm-dvm.desktop b/qubes-rpc/qvm-dvm.desktop index ba34250..e293b01 100644 --- a/qubes-rpc/qvm-dvm.desktop +++ b/qubes-rpc/qvm-dvm.desktop @@ -1,10 +1,15 @@ [Desktop Entry] -Actions=QvmDvm; +Actions=QvmDvm;QvmViewDvm Type=Service X-KDE-ServiceTypes=KonqPopupMenu/Plugin,all/allfiles [Desktop Action QvmDvm] Exec=/usr/bin/qvm-open-in-dvm %U Icon=kget -Name=Open In DisposableVM +Name=Edit In DisposableVM + +[Desktop Action QvmViewDvm] +Exec=/usr/bin/qvm-open-in-dvm --view-only %U +Icon=kget +Name=View In DisposableVM diff --git a/qubes-rpc/qvm-open-in-dvm b/qubes-rpc/qvm-open-in-dvm index 84af921..8046858 100755 --- a/qubes-rpc/qvm-open-in-dvm +++ b/qubes-rpc/qvm-open-in-dvm @@ -20,10 +20,10 @@ # # -if ! [ $# = 1 ] ; then - echo "Usage: $0 filename" +if ! [ $# = 1 ] && ! [ $# = 2 ]; then + echo "Usage: $0 [--view-only] filename" exit 1 fi # shellcheck disable=SC2016 -exec qvm-open-in-vm '$dispvm' "$1" +exec qvm-open-in-vm '$dispvm' "$@" diff --git a/qubes-rpc/qvm-open-in-vm b/qubes-rpc/qvm-open-in-vm index d109e6b..ead9acf 100755 --- a/qubes-rpc/qvm-open-in-vm +++ b/qubes-rpc/qvm-open-in-vm @@ -20,16 +20,37 @@ # # -if ! [ $# = 2 ] ; then - echo "Usage: $0 vmname filename" - exit 1 +usage() { + echo "Usage: $0 [--view-only] vmname filename" + exit 2 +} + +qopen_opts= +target= +filename= + +while [ $# -gt 0 ]; do + if [ "x$1" = "x--view-only" ]; then + qopen_opts=--view-only + elif [ -z "$target" ]; then + target="$1" + elif [ -z "$filename" ]; then + filename="$1" + else + usage + fi + shift +done + +if [ -z "$target" ] || [ -z "$filename" ]; then + usage fi -case "$2" in +case "$filename" in *://*) - exec /usr/lib/qubes/qrexec-client-vm "$1" qubes.OpenURL /bin/echo "$2" + exec /usr/lib/qubes/qrexec-client-vm "$target" qubes.OpenURL /bin/echo "$filename" ;; *) - exec /usr/lib/qubes/qrexec-client-vm "$1" qubes.OpenInVM "/usr/lib/qubes/qopen-in-vm" "$2" + exec /usr/lib/qubes/qrexec-client-vm "$target" qubes.OpenInVM "/usr/lib/qubes/qopen-in-vm" $qopen_opts "$filename" ;; esac diff --git a/qubes-rpc/qvm_copy_nautilus.py b/qubes-rpc/qvm_copy_nautilus.py index 30c990a..2a452e2 100755 --- a/qubes-rpc/qvm_copy_nautilus.py +++ b/qubes-rpc/qvm_copy_nautilus.py @@ -26,11 +26,9 @@ class CopyToAppvmItemExtension(GObject.GObject, Nautilus.MenuProvider): def on_menu_item_clicked(self, menu, files): '''Called when user chooses files though Nautilus context menu. ''' - for file_obj in files: - - # Check if file still exists - if file_obj.is_gone(): - return - - gio_file = file_obj.get_location() - subprocess.call(['/usr/lib/qubes/qvm-copy-to-vm.gnome', gio_file.get_path()]) + cmd = [file_obj.get_location().get_path() + for file_obj in files + # Check if file is not gone + if not file_obj.is_gone()] + cmd.insert(0, '/usr/lib/qubes/qvm-copy-to-vm.gnome') + subprocess.call(cmd) diff --git a/qubes-rpc/qvm_dvm_nautilus.py b/qubes-rpc/qvm_dvm_nautilus.py index a4311a4..7614396 100755 --- a/qubes-rpc/qvm_dvm_nautilus.py +++ b/qubes-rpc/qvm_dvm_nautilus.py @@ -17,15 +17,24 @@ class OpenInDvmItemExtension(GObject.GObject, Nautilus.MenuProvider): if not files: return - menu_item = Nautilus.MenuItem(name='QubesMenuProvider::OpenInDvm', - label='Open In DisposableVM', + menu_item1 = Nautilus.MenuItem(name='QubesMenuProvider::OpenInDvm', + label='Edit In DisposableVM', tip='', icon='') - menu_item.connect('activate', self.on_menu_item_clicked, files) - return menu_item, + menu_item1.connect('activate', self.on_menu_item_clicked, files) - def on_menu_item_clicked(self, menu, files): + menu_item2 = Nautilus.MenuItem(name='QubesMenuProvider::ViewInDvm', + label='View In DisposableVM', + tip='', + icon='') + + menu_item2.connect('activate', + self.on_menu_item_clicked, + files, True) + return menu_item1, menu_item2, + + def on_menu_item_clicked(self, menu, files, view_only=False): '''Called when user chooses files though Nautilus context menu. ''' for file_obj in files: @@ -38,6 +47,11 @@ class OpenInDvmItemExtension(GObject.GObject, Nautilus.MenuProvider): # Use subprocess.DEVNULL in python >= 3.3 devnull = open(os.devnull, 'wb') + command = ['nohup', '/usr/bin/qvm-open-in-dvm'] + if view_only: + command.append('--view-only') + command.append(gio_file.get_path()) # Use Popen instead of subprocess.call to spawn the process - Popen(['nohup', '/usr/bin/qvm-open-in-dvm', gio_file.get_path()], stdout=devnull, stderr=devnull) + Popen(command, stdout=devnull, stderr=devnull) + devnull.close() diff --git a/qubes-rpc/qvm_move_nautilus.py b/qubes-rpc/qvm_move_nautilus.py index 46a1639..25de579 100755 --- a/qubes-rpc/qvm_move_nautilus.py +++ b/qubes-rpc/qvm_move_nautilus.py @@ -26,11 +26,9 @@ class MoveToAppvmItemExtension(GObject.GObject, Nautilus.MenuProvider): def on_menu_item_clicked(self, menu, files): '''Called when user chooses files though Nautilus context menu. ''' - for file_obj in files: - - # Check if file still exists - if file_obj.is_gone(): - return - - gio_file = file_obj.get_location() - subprocess.call(['/usr/lib/qubes/qvm-move-to-vm.gnome', gio_file.get_path()]) + cmd = [file_obj.get_location().get_path() + for file_obj in files + # Check if file is not gone + if not file_obj.is_gone()] + cmd.insert(0, '/usr/lib/qubes/qvm-move-to-vm.gnome') + subprocess.call(cmd) diff --git a/qubes-rpc/tar2qfile.c b/qubes-rpc/tar2qfile.c index a88c02d..b0786d2 100644 --- a/qubes-rpc/tar2qfile.c +++ b/qubes-rpc/tar2qfile.c @@ -708,6 +708,7 @@ ustar_rd (int fd, struct file_header * untrusted_hdr, char *buf, struct stat * s // Split the path in directories and recompose it incrementally char * last_token = strtok(dirbuf,"/"); char * token = strtok(NULL, "/"); + size_t len_last_token = 0; while (token != NULL) { #ifdef DEBUG @@ -715,21 +716,22 @@ ustar_rd (int fd, struct file_header * untrusted_hdr, char *buf, struct stat * s #endif // Recompose the path based on last discovered directory + len_last_token = strlen(last_token); if (path == NULL) { - path = malloc(sizeof (char) * (strlen(last_token)+1)); + path = malloc(sizeof (char) * (len_last_token+1)); if (path == NULL) return MEMORY_ALLOC_FAILED; - path = strncpy(path, last_token, strlen(last_token)); - path[strlen(last_token)] = '\0'; + path = memcpy(path, last_token, len_last_token); + path[len_last_token] = '\0'; } else { pathsize = strlen(path); - path = realloc(path, sizeof (char) * (strlen(path)+1+strlen(last_token)+1)); + path = realloc(path, sizeof (char) * (strlen(path)+1+len_last_token+1)); if (path == NULL) return MEMORY_ALLOC_FAILED; path[pathsize] = '/'; - strncpy(path+pathsize+1, last_token, strlen(last_token)); - path[pathsize+strlen(last_token)+1] = '\0'; + memcpy(path+pathsize+1, last_token, len_last_token); + path[pathsize+len_last_token+1] = '\0'; } #ifdef DEBUG fprintf(stderr,"Path is %s\n",path); @@ -762,7 +764,8 @@ ustar_rd (int fd, struct file_header * untrusted_hdr, char *buf, struct stat * s dirs_headers_sent[n_dirs-1] = malloc(sizeof (char) * (strlen(path)+1)); if (dirs_headers_sent[n_dirs-1] == NULL) return MEMORY_ALLOC_FAILED; - strncpy(dirs_headers_sent[n_dirs-1], path, strlen(path)+1); + + memcpy(dirs_headers_sent[n_dirs-1], path, strlen(path)+1); // Initialize the qfile headers for the current directory path dir_header.namelen = strlen(path)+1; diff --git a/qubes-rpc/vm-file-editor.c b/qubes-rpc/vm-file-editor.c index 55594d2..45004e8 100644 --- a/qubes-rpc/vm-file-editor.c +++ b/qubes-rpc/vm-file-editor.c @@ -19,217 +19,227 @@ static const char *cleanup_dirname = NULL; static void cleanup_file(void) { - if (cleanup_filename) { - if (unlink(cleanup_filename) < 0) - fprintf(stderr, "Failed to remove file at exit\n"); - cleanup_filename = NULL; - } - if (cleanup_dirname) { - if (rmdir(cleanup_dirname) < 0) - fprintf(stderr, "Failed to remove directory at exit\n"); - cleanup_dirname = NULL; - } + if (cleanup_filename) { + if (unlink(cleanup_filename) < 0) + fprintf(stderr, "Failed to remove file at exit\n"); + cleanup_filename = NULL; + } + if (cleanup_dirname) { + if (rmdir(cleanup_dirname) < 0) + fprintf(stderr, "Failed to remove directory at exit\n"); + cleanup_dirname = NULL; + } } const char *gettime(void) { - static char retbuf[60]; - struct timeval tv; - gettimeofday(&tv, NULL); - snprintf(retbuf, sizeof(retbuf), "%lld.%06lld", - (long long) tv.tv_sec, (long long) tv.tv_usec); - return retbuf; + static char retbuf[60]; + struct timeval tv; + gettimeofday(&tv, NULL); + snprintf(retbuf, sizeof(retbuf), "%lld.%06lld", + (long long) tv.tv_sec, (long long) tv.tv_usec); + return retbuf; } static char *get_directory(void) { - const char *remote_domain; - char *dir; - size_t len; - char *ret; + const char *remote_domain; + char *dir; + size_t len; + char *ret; - remote_domain = getenv("QREXEC_REMOTE_DOMAIN"); - if (!remote_domain) { - fprintf(stderr, "Cannot get remote domain name\n"); - exit(1); - } - if (!*remote_domain || index(remote_domain, '/')) - goto fail; - if (!strcmp(remote_domain, ".") || !strcmp(remote_domain, "..")) - goto fail; + remote_domain = getenv("QREXEC_REMOTE_DOMAIN"); + if (!remote_domain) { + fprintf(stderr, "Cannot get remote domain name\n"); + exit(1); + } + if (!*remote_domain || index(remote_domain, '/')) + goto fail; + if (!strcmp(remote_domain, ".") || !strcmp(remote_domain, "..")) + goto fail; - len = strlen("/tmp/-XXXXXX")+strlen(remote_domain)+1; - dir = malloc(len); - if (!dir) { - fprintf(stderr, "Cannot allocate memory\n"); - exit(1); - } - snprintf(dir, len, "/tmp/%s-XXXXXX", remote_domain); + len = strlen("/tmp/-XXXXXX")+strlen(remote_domain)+1; + dir = malloc(len); + if (!dir) { + fprintf(stderr, "Cannot allocate memory\n"); + exit(1); + } + snprintf(dir, len, "/tmp/%s-XXXXXX", remote_domain); - ret = mkdtemp(dir); - if (ret == NULL) { - perror("mkdtemp"); - exit(1); - } - cleanup_dirname = strdup(ret); - return ret; + ret = mkdtemp(dir); + if (ret == NULL) { + perror("mkdtemp"); + exit(1); + } + cleanup_dirname = strdup(ret); + return ret; fail: - fprintf(stderr, "Invalid remote domain name: %s\n", remote_domain); - exit(1); + fprintf(stderr, "Invalid remote domain name: %s\n", remote_domain); + exit(1); } -char *get_filename(void) +char *get_filename(int *view_only) { - char buf[DVM_FILENAME_SIZE]; - static char *retname; - int i; - char *directory; - size_t len; + char buf[DVM_FILENAME_SIZE]; + char *fname = buf; + static char *retname; + int i; + char *directory; + size_t len; - directory = get_directory(); - if (!read_all(0, buf, sizeof(buf))) - exit(1); - buf[DVM_FILENAME_SIZE-1] = 0; - if (index(buf, '/')) { - fprintf(stderr, "filename contains /"); - exit(1); - } - for (i=0; buf[i]!=0; i++) { - // replace some characters with _ (eg mimeopen have problems with some of them) - if (index(" !?\"#$%^&*()[]<>;`~|", buf[i])) - buf[i]='_'; - } - len = strlen(directory)+1+strlen(buf)+1; - retname = malloc(len); - if (!retname) { - fprintf(stderr, "Cannot allocate memory\n"); - exit(1); - } - snprintf(retname, len, "%s/%s", directory, buf); - free(directory); - return retname; + directory = get_directory(); + if (!read_all(0, buf, sizeof(buf))) + exit(1); + buf[DVM_FILENAME_SIZE-1] = 0; + if (index(buf, '/')) { + fprintf(stderr, "filename contains /"); + exit(1); + } + for (i=0; buf[i]!=0; i++) { + // replace some characters with _ (eg mimeopen have problems with some of them) + if (index(" !?\"#$%^&*()[]<>;`~|", buf[i])) + buf[i]='_'; + } + if (strncmp(buf, DVM_VIEW_ONLY_PREFIX, strlen(DVM_VIEW_ONLY_PREFIX)) == 0) { + *view_only = 1; + fname += strlen(DVM_VIEW_ONLY_PREFIX); + } + len = strlen(directory)+1+strlen(fname)+1; + retname = malloc(len); + if (!retname) { + fprintf(stderr, "Cannot allocate memory\n"); + exit(1); + } + snprintf(retname, len, "%s/%s", directory, fname); + free(directory); + return retname; } void copy_file_by_name(const char *filename) { - int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0600); - if (fd < 0) { - perror("open file"); - exit(1); - } - /* we now have created a new file, ensure we delete it at the end */ - cleanup_filename = strdup(filename); - atexit(cleanup_file); - if (!copy_fd_all(fd, 0)) + int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (fd < 0) { + perror("open file"); exit(1); - close(fd); + } + /* we now have created a new file, ensure we delete it at the end */ + cleanup_filename = strdup(filename); + atexit(cleanup_file); + if (!copy_fd_all(fd, 0)) + exit(1); + close(fd); } void send_file_back(const char * filename) { - int fd = open(filename, O_RDONLY); - if (fd < 0) { - perror("open file"); - exit(1); - } - if (!copy_fd_all(1, fd)) - exit(1); - close(fd); - close(1); + int fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("open file"); + exit(1); + } + if (!copy_fd_all(1, fd)) + exit(1); + close(fd); + close(1); } int main() { - struct stat stat_pre, stat_post, session_stat; - char *filename = get_filename(); - int child, status, log_fd, null_fd; - FILE *waiter_pidfile; + struct stat stat_pre, stat_post, session_stat; + int view_only = 0; + char *filename = get_filename(&view_only); + int child, status, log_fd, null_fd; + FILE *waiter_pidfile; - copy_file_by_name(filename); - if (stat(filename, &stat_pre)) { - perror("stat pre"); - exit(1); - } + copy_file_by_name(filename); + if (view_only) { + // mark file as read-only so applications will signal it to the user + chmod(filename, 0400); + } + if (stat(filename, &stat_pre)) { + perror("stat pre"); + exit(1); + } #ifdef DEBUG - fprintf(stderr, "time=%s, waiting for qubes-session\n", gettime()); + fprintf(stderr, "time=%s, waiting for qubes-session\n", gettime()); #endif - // wait for X server to starts (especially in DispVM) - if (stat("/tmp/qubes-session-env", &session_stat)) { - switch (child = fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - waiter_pidfile = fopen("/tmp/qubes-session-waiter", "a"); - if (waiter_pidfile == NULL) { - perror("fopen waiter_pidfile"); - exit(1); - } - fprintf(waiter_pidfile, "%d\n", getpid()); - fclose(waiter_pidfile); - // check the second time, to prevent race - if (stat("/tmp/qubes-session-env", &session_stat)) { - // wait for qubes-session notify - pause(); - } - exit(0); - default: - waitpid(child, &status, 0); - if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { - //propagate exit code from child - exit(WEXITSTATUS(status)); - } - } - } + // wait for X server to starts (especially in DispVM) + if (stat("/tmp/qubes-session-env", &session_stat)) { + switch (child = fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + waiter_pidfile = fopen("/tmp/qubes-session-waiter", "a"); + if (waiter_pidfile == NULL) { + perror("fopen waiter_pidfile"); + exit(1); + } + fprintf(waiter_pidfile, "%d\n", getpid()); + fclose(waiter_pidfile); + // check the second time, to prevent race + if (stat("/tmp/qubes-session-env", &session_stat)) { + // wait for qubes-session notify + pause(); + } + exit(0); + default: + waitpid(child, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { + //propagate exit code from child + exit(WEXITSTATUS(status)); + } + } + } #ifdef DEBUG - fprintf(stderr, "time=%s, starting editor\n", gettime()); + fprintf(stderr, "time=%s, starting editor\n", gettime()); #endif - switch (child = fork()) { - case -1: - perror("fork"); - exit(1); - case 0: - null_fd = open("/dev/null", O_RDONLY); - dup2(null_fd, 0); - close(null_fd); + switch (child = fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + null_fd = open("/dev/null", O_RDONLY); + dup2(null_fd, 0); + close(null_fd); - log_fd = open("/tmp/mimeopen.log", O_CREAT | O_APPEND, 0666); - if (log_fd == -1) { - perror("open /tmp/mimeopen.log"); - exit(1); - } - dup2(log_fd, 1); - close(log_fd); + log_fd = open("/tmp/mimeopen.log", O_CREAT | O_APPEND, 0666); + if (log_fd == -1) { + perror("open /tmp/mimeopen.log"); + exit(1); + } + dup2(log_fd, 1); + close(log_fd); - setenv("HOME", USER_HOME, 1); - setenv("DISPLAY", ":0", 1); - execl("/usr/bin/qubes-open", "qubes-open", filename, (char*)NULL); - perror("execl"); - exit(1); - default: - waitpid(child, &status, 0); - if (status != 0) { - char cmd[512]; + setenv("HOME", USER_HOME, 1); + setenv("DISPLAY", ":0", 1); + execl("/usr/bin/qubes-open", "qubes-open", filename, (char*)NULL); + perror("execl"); + exit(1); + default: + waitpid(child, &status, 0); + if (status != 0) { + char cmd[512]; #ifdef USE_KDIALOG - snprintf(cmd, sizeof(cmd), - "HOME=/home/user DISPLAY=:0 /usr/bin/kdialog --sorry 'Unable to handle mimetype of the requested file (exit status: %d)!' > /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 = 18 -# Fedora >= 18 defaults to firewalld, which isn't supported nor needed by Qubes + Conflicts: firewalld -%endif -Requires: xdg-utils +Requires: xdg-utils Requires: qubes-utils >= 3.1.3 Requires: qubes-utils-libs >= 4.0.16 Requires: initscripts @@ -137,10 +129,11 @@ Requires: python2-qubesdb Requires: ImageMagick Requires: librsvg2-tools Requires: zenity +Requires: dconf Requires: qubes-core-agent-qrexec Requires: qubes-libvchan Requires: qubes-db-vm -%if 0%{fedora} >= 23 +%if 0%{?fedora} >= 23 Requires: python3-dnf-plugins-qubes-hooks %else Requires: python2-dnf-plugins-qubes-hooks @@ -158,8 +151,13 @@ BuildRequires: pandoc BuildRequires: xen-devel BuildRequires: libX11-devel BuildRequires: qubes-utils-devel >= 3.1.3 -BuildRequires: qubes-libvchan-%{backend_vmm}-devel +BuildRequires: qubes-libvchan-@BACKEND_VMM@-devel BuildRequires: pam-devel +%if 0%{?rhel} >= 7 +BuildRequires: python-setuptools +%endif +BuildRequires: systemd +Source0: %{name}-%{version}.tar.gz %description The Qubes core files for installation inside a Qubes VM. @@ -174,29 +172,15 @@ DNF plugin for Qubes specific post-installation actions: * notify dom0 that updates were installed * refresh applications shortcut list -%if 0%{fedora} >= 23 -%package -n python3-dnf-plugins-qubes-hooks +%package -n python%{python3_pkgversion}-dnf-plugins-qubes-hooks Summary: DNF plugin for Qubes specific post-installation actions -BuildRequires: python3-devel -%{?python_provide:%python_provide python3-dnf-plugins-qubes-hooks} +BuildRequires: python%{python3_pkgversion}-devel +%{?python_provide:%python_provide python%{python3_pkgversion}-dnf-plugins-qubes-hooks} -%description -n python3-dnf-plugins-qubes-hooks +%description -n python%{python3_pkgversion}-dnf-plugins-qubes-hooks DNF plugin for Qubes specific post-installation actions: * notify dom0 that updates were installed * refresh applications shortcut list -%endif - -%if 0%{?rhel} >= 7 -%package -n python34-dnf-plugins-qubes-hooks -Summary: DNF plugin for Qubes specific post-installation actions -BuildRequires: python34-devel -%{?python_provide:%python_provide python34-dnf-plugins-qubes-hooks} - -%description -n python34-dnf-plugins-qubes-hooks -DNF plugin for Qubes specific post-installation actions: - * notify dom0 that updates were installed - * refresh applications shortcut list -%endif %package qrexec Summary: Qubes qrexec agent @@ -277,21 +261,15 @@ Requires: Thunar %description thunar Thunar support for Qubes VM tools -%define _builddir %(pwd) - %define kde_service_dir /usr/share/kde4/services %define kde5_service_dir /usr/share/kservices5/ServiceMenus %prep -# we operate on the current directory, so no need to unpack anything -# symlink is to generate useful debuginfo packages -rm -f %{name}-%{version} -ln -sf . %{name}-%{version} -%setup -T -D +%setup -q %build for dir in qubes-rpc qrexec misc; do - (cd $dir; make) + make -C $dir BACKEND_VMM=@BACKEND_VMM@ done make -C doc manpages @@ -353,6 +331,7 @@ for F in plymouth-shutdown prefdm splash-manager start-ttys tty ; do fi done +chgrp user /var/lib/qubes/dom0-updates # Remove old firmware updates link if [ -L /lib/firmware/updates ]; then @@ -365,6 +344,12 @@ if test -f /etc/yum.conf && ! grep -q '/etc/yum\.conf\.d/qubes-proxy\.conf' /etc echo 'include=file:///etc/yum.conf.d/qubes-proxy.conf' >> /etc/yum.conf fi +if ! [ -r /etc/dconf/profile/user ]; then + mkdir -p /etc/dconf/profile + echo "user-db:user" >> /etc/dconf/profile/user + echo "system-db:local" >> /etc/dconf/profile/user +fi + dconf update &> /dev/null || : # And actually setup the proxy usage in package managers @@ -471,7 +456,7 @@ sed 's/^net.ipv4.ip_forward.*/#\0/' -i /etc/sysctl.conf %post qrexec %systemd_post qubes-qrexec-agent.service -%post thunar +%post thunar if [ "$1" = 1 ]; then # There is no system-wide Thunar custom actions. There is only a default # file and a user file created from the default one. Qubes actions need @@ -508,7 +493,7 @@ fi %preun qrexec %systemd_preun qubes-qrexec-agent.service -%postun thunar +%postun thunar if [ "$1" = 0 ]; then if [ -f /etc/xdg/Thunar/uca.xml ] ; then mv /etc/xdg/Thunar/uca.xml{,.uninstall} @@ -609,10 +594,7 @@ rm -f %{name}-%{version} %config(noreplace) /etc/yum.repos.d/qubes-r4.repo /etc/yum/pluginconf.d/yum-qubes-hooks.conf %config(noreplace) /etc/dnf/plugins/qubes-hooks.conf -%if 0%{?fedora} >= 23 -%config(noreplace) /etc/dconf/profile/user %config(noreplace) /etc/dconf/db/local.d/dpi -%endif /usr/lib/systemd/system/user@.service.d/90-session-stop-timeout.conf /usr/sbin/qubes-serial-login /usr/bin/qvm-copy-to-vm @@ -626,6 +608,7 @@ rm -f %{name}-%{version} /usr/bin/qvm-sync-clock /usr/bin/xenstore-watch-qubes /usr/bin/qubes-desktop-run +/usr/bin/qubes-run-terminal /usr/bin/qubes-open /usr/bin/qubes-session-autostart %dir /usr/lib/qubes @@ -664,7 +647,7 @@ rm -f %{name}-%{version} /usr/lib/qubes/init/functions %dir /usr/lib/qubes-bind-dirs.d /usr/lib/qubes-bind-dirs.d/30_cron.conf -/usr/lib/python2.7/site-packages/qubesxdg.py* +/usr/share/applications/qubes-run-terminal.desktop /usr/share/qubes/serial.conf /usr/share/glib-2.0/schemas/20_org.gnome.settings-daemon.plugins.updates.qubes.gschema.override /usr/share/glib-2.0/schemas/20_org.gnome.nautilus.qubes.gschema.override @@ -678,6 +661,7 @@ rm -f %{name}-%{version} %{python_sitelib}/qubesagent/__init__.py* %{python_sitelib}/qubesagent/firewall.py* %{python_sitelib}/qubesagent/test_firewall.py* +%{python_sitelib}/qubesagent/xdg.py* /usr/share/qubes/mime-override/globs /usr/share/qubes/qubes-master-key.asc @@ -687,7 +671,7 @@ rm -f %{name}-%{version} %files -n python2-dnf-plugins-qubes-hooks %{python2_sitelib}/dnf-plugins/* -%if 0%{fedora} >= 23 +%if 0%{?fedora} >= 23 %files -n python3-dnf-plugins-qubes-hooks %{python3_sitelib}/dnf-plugins/* %endif @@ -817,9 +801,6 @@ for svc in %qubes_services ; do fi done -# dropped services -chkconfig qubes-netwatcher off || : - # TODO: make this not display the silly message about security context... sed -i s/^id:.:initdefault:/id:3:initdefault:/ /etc/inittab @@ -967,3 +948,6 @@ if [ "x$changed" != "x" ] then systemctl daemon-reload fi + +%changelog +@CHANGELOG@ diff --git a/run-tests b/run-tests index 00e7497..5f7e7d2 100755 --- a/run-tests +++ b/run-tests @@ -10,4 +10,4 @@ export PYTHONPATH [ -r version ] || ln -s ${ROOTDIR}/version ./ [ -r setup.py ] || ln -s ${ROOTDIR}/setup.py ./ "${PYTHON}" ./setup.py egg_info --egg-base "${TESTPYTHONPATH}" -"${PYTHON}" -m coverage run -m unittest discover -p '*.py' -v "$@" +"${PYTHON}" -m coverage run -m unittest discover -p 'test_*.py' -v "$@" diff --git a/version b/version index f05f0cb..6634c5d 100644 --- a/version +++ b/version @@ -1 +1 @@ -4.0.23 +4.0.31 diff --git a/vm-systemd/75-qubes-vm.preset b/vm-systemd/75-qubes-vm.preset index d13850d..7c0d3c5 100644 --- a/vm-systemd/75-qubes-vm.preset +++ b/vm-systemd/75-qubes-vm.preset @@ -56,6 +56,7 @@ disable smartd.service disable upower.service disable colord.service disable wpa_supplicant@.service +disable dkms.service # Fedora only services disable cpuspeed.service diff --git a/vm-systemd/qubes-firewall.service b/vm-systemd/qubes-firewall.service index 3fb725c..b98745f 100644 --- a/vm-systemd/qubes-firewall.service +++ b/vm-systemd/qubes-firewall.service @@ -5,6 +5,7 @@ After=qubes-iptables.service Before=qubes-network.service [Service] +Type=notify ExecStart=/usr/sbin/qubes-firewall [Install] diff --git a/vm-systemd/qubes-misc-post.service b/vm-systemd/qubes-misc-post.service index 09a2f84..a0b2596 100644 --- a/vm-systemd/qubes-misc-post.service +++ b/vm-systemd/qubes-misc-post.service @@ -1,6 +1,6 @@ [Unit] Description=Qubes misc post-boot actions -After=network-pre.target qubes-mount-dirs.service qubes-network.service qubes-firewall.service qubes-netwatcher.service +After=network-pre.target qubes-mount-dirs.service qubes-network.service qubes-firewall.service [Service] Type=oneshot