Skip to content

Commit

Permalink
Move MIME handling to systemd services (boot provisioning)
Browse files Browse the repository at this point in the history
Fixes freedomofpress/securedrop-workstation#1042, complemented
by freedomofpress/securedrop-workstation#1043.

Implemention reasoning:
- Failure to setup leads shutdown to make this security-critical
  component loud on failures
- Running in disp. templates fails (e.g. sd-viewer) to prevent
  populating user's home directory, thus contaminating all
  disposables based on them.
- MIME-handling behavior set via qubesdb - pass vm-specific data
  via the vm-config qubes feature (accessible through QubesDB) [1].
- Started after rsyslog.service to allow failure logging

[1]: https://dev.qubes-os.org/projects/core-admin-client/en/latest/manpages/qvm-features.html#vm-config
  • Loading branch information
deeplow committed Jun 3, 2024
1 parent e50e0ff commit 21a1ec3
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 3 deletions.
4 changes: 2 additions & 2 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Source: securedrop-client
Section: unknown
Priority: optional
Maintainer: SecureDrop Team <[email protected]>
Build-Depends: debhelper-compat (= 11), dh-apparmor, python3-virtualenv, libssl-dev, pkg-config, libclang-dev, qubesdb-dev
Build-Depends: debhelper-compat (= 11), dh-apparmor, dh-exec, python3-virtualenv, libssl-dev, pkg-config, libclang-dev, qubesdb-dev
Standards-Version: 3.9.8
Homepage: https://github.com/freedomofpress/securedrop-client
X-Python3-Version: >= 3.5
Expand Down Expand Up @@ -55,7 +55,7 @@ Description: Whonix configuration for SecureDrop.

Package: securedrop-workstation-config
Architecture: all
Depends: rsyslog, mailcap, apparmor, nautilus, securedrop-keyring, xfce4-terminal
Depends: ${python3:Depends}, python3-qubesdb, rsyslog, mailcap, apparmor, nautilus, securedrop-keyring, xfce4-terminal
Description: This is the SecureDrop workstation template configuration package.
This package provides dependencies and configuration for the Qubes SecureDrop workstation VM Templates.

Expand Down
1 change: 1 addition & 0 deletions debian/rules
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ override_dh_installsystemd:
dh_installsystemd --name securedrop-log-server
dh_installsystemd --name securedrop-logging-disabled
dh_installsystemd --name securedrop-whonix-config
dh_installsystemd --name securedrop-mime-handling
4 changes: 3 additions & 1 deletion debian/securedrop-workstation-config.install
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/dh-exec --with=install
workstation-config/mailcap.default opt/sdw/
workstation-config/mimeapps.list.sd-viewer opt/sdw/
workstation-config/mimeapps.list.sd-app opt/sdw/
workstation-config/mimeapps.list.sd-devices-dvm opt/sdw/
workstation-config/mimeapps.list.sd-devices opt/sdw/
workstation-config/open-in-dvm.desktop opt/sdw/
workstation-config/paxctld.conf opt/sdw/
workstation-config/securedrop-mime-handling.py => usr/bin/securedrop-mime-handling
6 changes: 6 additions & 0 deletions debian/securedrop-workstation-config.lintian-overrides
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ securedrop-workstation-config: maintainer-script-calls-systemctl [postinst:28]

# We're not shipping CDs, so this is fine
securedrop-workstation-config: package-has-long-file-name

# We don't care
securedrop-workstation-config: no-manual-page

# FIXME: missing a python3 dependency
securedrop-workstation-config: python3-script-but-no-python3-dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[Unit]
Description=Securedrop Mimetype Handling Override
ConditionPathExists=/var/run/qubes-service/securedrop-mime-handling
OnFailure=systemd-halt.service
After=rsyslog.service

[Service]
Type=oneshot
User=user
ExecStart=/usr/bin/securedrop-mime-handling
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
File renamed without changes.
86 changes: 86 additions & 0 deletions workstation-config/securedrop-mime-handling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/python3
##
# securedrop-mime-handling
# =====================
#
# Overrides mimetype handling for certain VMs. Instead of relying on the
# /usr/share/applications (system volume), we instead use /home/user/.local/share/
# to be provisioned on boot by systemd.
##

import logging
import logging.handlers
import sys
from pathlib import Path

from qubesdb import QubesDB

# Configure logging
syslog_handler = logging.handlers.SysLogHandler(address="/dev/log")

stdout_handler = logging.StreamHandler(sys.stdout)
logging.basicConfig(level=logging.INFO, handlers=[syslog_handler, stdout_handler])
logger = logging.getLogger()


def create_symlink_strict(source_path_str, target_path_str, overwrite_expected=False):
"""
Creates a symlink, but warning when idempotence is required and failing if
link source already exsits.
"""
source_path = Path(source_path_str)
target_path = Path(target_path_str)
target_path.resolve(strict=True) # raise exception if does not exist

try:
source_path.symlink_to(target_path)
except FileExistsError as e:
if source_path.is_symlink():
if source_path.readlink() == target_path:
if not overwrite_expected:
# Harmless situation, yet not ideal (idempotency required)
logger.warning(f"'{source_path}' existed already (unexpected)")
else:
logger.error(f"'{source_path}' existed already and is linked to the wrong file")
raise e
else:
logger.error(f"'{source_path}' existed already")
raise e


def get_mime_handling():
mime_handling = QubesDB().read("/vm-config/SD_MIME_HANDLING")
if mime_handling is None or len(mime_handling) == 0:
raise RuntimeError("'SD_MIME_HANDLING' qubesdb vm-config is not set")
return mime_handling.decode()


def main():
# Should fail on DVM templates to avoid tainting the disposables' home.
# In practice we cannot detect this from within, so we have to hard-code
# sd-app as the only valid non-disposable.
persistent_home = QubesDB().read("/qubes-vm-persistence").decode() != "none"
vm_name = QubesDB().read("/name").decode()
if persistent_home and vm_name != "sd-app":
sys.exit(1)

# Ensure applications open with the correct tool (or disposable qube)
mime_handling = get_mime_handling()
mimeapps_override_path = Path(f"/opt/sdw/mimeapps.list.{mime_handling}")
mimeapps_override_path.resolve(strict=True)
user_apps_dir_path = Path("/home/user/.local/share/applications")
user_apps_dir_path.mkdir(mode=0o755, parents=True, exist_ok=True)
create_symlink_strict(
user_apps_dir_path / "mimeapps.list",
mimeapps_override_path,
overwrite_expected=persistent_home,
)

# Fallback mechanism if MIME type lookup fails in tools like xdg-open
create_symlink_strict(
"/home/user/.mailcap", "/opt/sdw/mailcap.default", overwrite_expected=persistent_home
)


if __name__ == "__main__":
main()

0 comments on commit 21a1ec3

Please sign in to comment.