From 440920be2a72761dcb9aceaee76d35f5247cc9c8 Mon Sep 17 00:00:00 2001 From: mickael e Date: Mon, 27 May 2019 10:33:08 -0400 Subject: [PATCH] Use x-sd-export mimetype and full disk encryption for export This is based off of early decryption flow introduced in 9b4c37e7756ea037106690fc0e8177e5dab0db68 --- Makefile | 4 +- dom0/sd-export-files.sls | 34 +++++++--- scripts/list-vms | 2 +- sd-export/application-x-sd-export.xml | 7 ++ sd-export/send-to-usb | 92 ++++++++++++++++++++------- sd-export/send-to-usb.desktop | 5 ++ 6 files changed, 109 insertions(+), 35 deletions(-) create mode 100644 sd-export/application-x-sd-export.xml mode change 100644 => 100755 sd-export/send-to-usb create mode 100644 sd-export/send-to-usb.desktop diff --git a/Makefile b/Makefile index 5463780db..02fe2185c 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ remove-sd-gpg: assert-dom0 ## Destroys SD GPG keystore VM @./scripts/destroy-vm sd-gpg remove-sd-export: assert-dom0 ## Destroys SD EXPORT VMs - @qvm-kill sd-export-usb + @qvm-kill sd-export-usb || true @qvm-usb detach sd-export-usb || true @./scripts/destroy-vm sd-export-usb @./scripts/destroy-vm sd-export-dvm @@ -143,7 +143,7 @@ prep-dom0: prep-salt # Copies dom0 config files for VM updates list-vms: ## Prints all Qubes VMs managed by Workstation salt config @./scripts/list-vms -destroy-all: remove-sd-export ## Destroys all VMs managed by Workstation salt config +destroy-all: ## Destroys all VMs managed by Workstation salt config @./scripts/list-vms | xargs ./scripts/destroy-vm # Explanation of the below shell command should it ever break. diff --git a/dom0/sd-export-files.sls b/dom0/sd-export-files.sls index 59484deed..99a0b90b9 100644 --- a/dom0/sd-export-files.sls +++ b/dom0/sd-export-files.sls @@ -20,13 +20,29 @@ sd-export-send-to-usb-script: - mode: 755 - makedirs: True -sd-export-template-mimetype: - file.blockreplace: - - name: /etc/mailcap - - prepend_if_not_found: False - - marker_start: "# ----- User Section Begins ----- #" - - marker_end: "# ----- User Section Ends ----- #" - - content: | - application/octet-stream; /usr/bin/send-to-usb '%s'; +sd-export-desktop-file: + file.managed: + - name: /usr/share/applications/send-to-usb.desktop + - source: salt://sd/sd-export/send-to-usb.desktop + - user: root + - group: root + - mode: 755 + - makedirs: True + cmd.run: + - name: sudo update-desktop-database /usr/share/applications + - require: + - file: sd-export-desktop-file + +sd-export-file-format: + file.managed: + - name: /usr/share/mime/packages/application-x-sd-export.xml + - source: salt://sd/sd-export/application-x-sd-export.xml + - user: root + - group: root + - mode: 755 + - makedirs: True cmd.run: - - name: sudo update-mime + - name: sudo update-mime-database /usr/share/mime + - require: + - file: sd-export-file-format + - file: sd-export-desktop-file diff --git a/scripts/list-vms b/scripts/list-vms index b83af2981..ef58a20c0 100755 --- a/scripts/list-vms +++ b/scripts/list-vms @@ -18,7 +18,7 @@ declare -a sd_workstation_vm_names=( sd-svs-disp-template sd-export-template sd-export-dvm - sd-export + sd-export-usb ) for vm in "${sd_workstation_vm_names[@]}" ; do diff --git a/sd-export/application-x-sd-export.xml b/sd-export/application-x-sd-export.xml new file mode 100644 index 000000000..9e36ef08b --- /dev/null +++ b/sd-export/application-x-sd-export.xml @@ -0,0 +1,7 @@ + + + + Archive for transfering files from the SecureDrop workstation to an external USB device. + + + diff --git a/sd-export/send-to-usb b/sd-export/send-to-usb old mode 100644 new mode 100755 index 0371af603..289d2b405 --- a/sd-export/send-to-usb +++ b/sd-export/send-to-usb @@ -1,38 +1,84 @@ #! /usr/bin/python3 + +import datetime +import json import os import shutil import subprocess import sys +import tarfile +import tempfile DEVICE = "/dev/sda1" -MOUNTPOINT = "/tmp/usb" +MOUNTPOINT = "/media/usb" +ENCRYPTED_DEVICE = "encrypted_volume" FILE = sys.argv[1] +folder_name = "" +encryption_method = "" +encryption_key = "" +target_folder = "sd-export-{}".format(datetime.datetime.now().isoformat()) + +tmpdir = tempfile.mkdtemp() if os.path.exists(FILE): - # mount target not created - if not os.path.exists(MOUNTPOINT): - os.makedirs(MOUNTPOINT) - # check if drive already mounted - rc = subprocess.call( - ["mountpoint", MOUNTPOINT], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL - ) - if rc: - # drive is not already mounted, so... - out = subprocess.run(["sudo", "mount", "-o", "uid=1000,gid=1000", DEVICE, MOUNTPOINT]) + try: + with tarfile.open(FILE) as tar: + tar.extractall(tmpdir) + except Exception as e: + # exit with 0 return code otherwise the os will attempt to open + # the file with another application + print("Error opening export bundle") + sys.exit(0) - rc = subprocess.call( - ["mountpoint", MOUNTPOINT], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL - ) - if rc: - # drive still not mounted - sys.exit(1) + try: + folder_name = os.path.basename(FILE).split(".")[0] + with open(os.path.join(tmpdir, folder_name, "metadata.json")) as json_data: + data = json.load(json_data) + encryption_method = data["encryption-method"] + encryption_key = data["encryption-key"] + except Exception as e: + print("Error parsing metadata") + sys.exit(0) - # copy files to drive (overwrites existing files) and unmount drive - shutil.copy(FILE, os.path.join(MOUNTPOINT, os.path.basename(FILE))) + # we only support luks for now + if encryption_method != "luks": + print("Unsupported export encryption") + sys.exit(0) + + # the luks device is not already unlocked + + if not os.path.exists(os.path.join("/dev/mapper/", ENCRYPTED_DEVICE)): + p = subprocess.Popen( + ["sudo", "cryptsetup", "luksOpen", DEVICE, ENCRYPTED_DEVICE], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + stdout_data = p.communicate(input=str.encode(encryption_key, "utf-8"))[0] + rc = p.returncode + if rc != 0: + print("Bad passphrase or luks error") + sys.exit(0) + + # mount target not created + if not os.path.exists(MOUNTPOINT): + out = subprocess.call(["sudo", "mkdir", MOUNTPOINT]) + out = subprocess.call(["sudo", "chown", "-R", "user:user", MOUNTPOINT]) + out = subprocess.call( + ["sudo", "mount", os.path.join("/dev/mapper/", ENCRYPTED_DEVICE), MOUNTPOINT] + ) - # sync to ensure the files are fully written before unmounting - subprocess.run(["sync"]) + # move files to drive (overrites files with same filename) and unmount drive + target_folder_path = os.path.join(MOUNTPOINT, target_folder) + subprocess.call(["mkdir", target_folder_path]) + export_data = os.path.join(tmpdir, folder_name, "export_data/") + shutil.move(export_data, target_folder_path) - # finally, unmount the USB device - subprocess.run(["sudo", "umount", MOUNTPOINT]) + # sync the filesystem, unmount drive and lock the luks volume + # we use call here to ensure they are blocking and avoid races + subprocess.call(["sync"]) + subprocess.call(["sudo", "umount", MOUNTPOINT]) + subprocess.call(["sudo", "cryptsetup", "luksClose", ENCRYPTED_DEVICE]) + # race condition when using shutils + subprocess.call(["rm", "-rf", tmpdir]) diff --git a/sd-export/send-to-usb.desktop b/sd-export/send-to-usb.desktop new file mode 100644 index 000000000..6521a92c9 --- /dev/null +++ b/sd-export/send-to-usb.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Type=Application +MimeType=application/x-sd-export +Name="Export SD submission to USB" +Exec=/usr/bin/send-to-usb