From 6bf666a534f5376ba813b0aaf8993691d2925496 Mon Sep 17 00:00:00 2001 From: Papoteur Date: Mon, 20 Jan 2020 09:42:52 +0100 Subject: Add encryption for persistent partition --- CHANGELOG | 14 +++++-- README.md | 7 +++- backend/magiback | 21 ++++++----- backend/raw_write.py | 101 +++++++++++++++++++++++++++++++++++++++++---------- lib/isodumper.py | 65 ++++++++++++++++++++++++--------- 5 files changed, 155 insertions(+), 53 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 239c7be..a950cb9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,11 @@ The modifications are: +IsoDumper 1.16 +-------------- + - add format exFAT for formatting the device + - add eject instruction after writing operations + - add option for creation of encrypted persistent partition, could be used for Mageia 8 and later + IsoDumper 1.15 -------------- - Rework udisks2 information usage to be more reliable @@ -15,7 +21,7 @@ IsoDumper 1.13 IsoDumper 1.11 -------------- - Raise error when an instance is already running - + IsoDumper 1.10 -------------- - Correction to sr.po to allow generation of all translation catalogs @@ -46,7 +52,7 @@ IsoDumper 1.05 IsoDumper 1.04 -------------- - Separate getting key and checking signature in a thread - - Review the logic of checking + - Review the logic of checking IsoDumper 1.03 -------------- @@ -99,7 +105,7 @@ IsoDumper 0.55 IsoDumper 0.54 ------------ - - Updating translations in Sapnish, Romanian, Sweadidh, Basque and Latvian + - Updating translations in Sapnish, Romanian, Sweadidh, Basque and Latvian - Adding links of the source in About dialog (mga#18405) IsoDumper 0.53 @@ -131,7 +137,7 @@ IsoDumper 0.50 ------------ - yui release. - + IsoDumper 0.48 ------------ - Better management of drive names (mga#18411). diff --git a/README.md b/README.md index 634a6ac..9fde56d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ isodumper A tool for USB sticks and removable memory devices like SD-cards. Features: -* dump ISO file on the device; +* dump ISO file on the device; optionnally create a persistent partition, encrypted or not, in the remaining space; * format the device in one partition of ext4, NTFS or FAT32 type; * backup the device as a whole. @@ -19,6 +19,9 @@ Requirements - udisks2 - libyui and declanation of ncurses, gtk or Qt - libyui-mga +- cryptsetup +- fdisk +- e2fsprogs License @@ -29,7 +32,7 @@ This software is distributed under the terms of the The [isodumper icon](isodumper.svg) is licensed under the terms of the [GNU Lesser General Public License version 2.1 (LGPLv2.1+)](COPYING.LGPL). -It is part of the Crystal Clear icon set by Everaldo Coelho. +It is created by Thimothée Giet. Authors -------- diff --git a/backend/magiback b/backend/magiback index 6bf0202..e7541c3 100755 --- a/backend/magiback +++ b/backend/magiback @@ -40,6 +40,7 @@ class Isodumper(raw_write.Dumper): + @@ -75,7 +76,7 @@ class Isodumper(raw_write.Dumper): self.signature_checked = False self.writing_perm = False self.writing_target = "" - + def do_write(self,source, target, size, dbus_context): self.finished.clear() @@ -90,19 +91,19 @@ class Isodumper(raw_write.Dumper): logging.debug(self.return_message) self.finished.set() - def do_persistence(self, target, label): + def do_persistence(self, target, label, key): self.finished.clear() if self.writing_perm and self.writing_target == target : - self._do_persistence(target, label) + self._do_persistence(target, label, key) else: self.return_message = "Persistence: Access denied" self.writing_perm = False self.writing_target = "" - + @property def done(self): return self.finished.wait(1) - + @property def message(self): return self.return_message @@ -128,13 +129,13 @@ class Isodumper(raw_write.Dumper): def get_sum(self, source): self.key_thread = threading.Thread(target=self._get_sum, args=(source,)) self.key_thread.start() - + def check_write(self,target, source): if hasattr(self, 'key_thread'): self.key_thread.join() self.thread = threading.Thread(target=self._check_write, args=(target, source,)) self.thread.start() - + def run(self): self.loop.run() @@ -142,7 +143,7 @@ def check_permission(action, dbus_context): """ Check permission """ return dbus_context.is_authorized(action, {'polkit.icon_name': 'isodumper.png',}, interactive=True) - + class ConfFile(object): """ @@ -161,7 +162,7 @@ class ConfFile(object): """ def __init__(self): super().__init__() - + def setName(self,file_name): self.file_name = file_name @@ -175,7 +176,7 @@ class ConfFile(object): break content += line return content - + def saveFile(self, tc, dbus_context): if check_permission('org.mageia.Magiback.write',dbus_context): try: diff --git a/backend/raw_write.py b/backend/raw_write.py index 699d1fc..9f06295 100755 --- a/backend/raw_write.py +++ b/backend/raw_write.py @@ -29,13 +29,14 @@ #import locale import os import io +import sys import gettext from subprocess import call, Popen, PIPE import hashlib import gnupg import time import logging - + class Dumper(object): def _do_write(self,source,target, b): @@ -150,6 +151,7 @@ class Dumper(object): message += _('Error, umount returned {}').format(str(retcode)) except OSError as e: message += _('Execution failed: {}').format(str(e)) + print(message, file=sys.stderr) logging.info(message) return not bool(retcode), message @@ -167,7 +169,7 @@ class Dumper(object): with open(sig_file, 'rb') as g: self.signature_found = True verified = gpg.verify_file(g, source + ".sha512") - if verified.valid: + if verified.valid: self.signature_checked = True logging.debug("signature checked") else: @@ -205,7 +207,7 @@ class Dumper(object): if checked > steps[indice]: self._progress = indice indice +=1 - checked+=1024 + checked+=1024 block = f.read(b-ncuts*1024) sha512func.update(block) sha512sumcalc=sha512func.hexdigest() @@ -220,36 +222,97 @@ class Dumper(object): self.return_message +="\n" + _("The sha512 sum check is OK and the sum is signed") else : self.return_message +="\n" + _("The sha512 sum check is OK but the signature can't be found") - else: + else: self.return_message +="\n" + _("/!\\The computed and stored sums don't match") #except: #pass self._progress = 100 - + logging.info(self.return_message) self.return_state = True self.finished.set() - def _do_persistence(self, target, label): + def _do_persistence(self, target, label, key): logging.debug("Start doing persistence partition") p = Popen(["fdisk",target], stdin = PIPE) p.communicate(input=b'n\np\n3\n\n\nw\n') # example mkfs.ext4 -L mgalive-persist /dev/sdf3 - process = Popen(['mkfs.ext4','-L', label, target+"3"]) - working=True - while working: - time.sleep(0.5) - process.poll() - rc=process.returncode - if rc is None: - working=True - else: - process = None - working= False - logging.debug("Persistence partition done") + + if key == "": + print("No key provided", file=sys.stderr) + process = Popen(['mkfs.ext4','-L', label, target+"3"]) + p.communicate() + working=True + while working: + time.sleep(0.5) + process.poll() + rc=process.returncode + if rc is None: + working=True + else: + process = None + working= False + logging.debug("Persistence partition done") + else: + # cryptsetup luksFormat /dev/sdb3 + print("Crypt key provided",file=sys.stderr) + base_target = os.path.basename(target) + "3" + process = Popen(['cryptsetup','luksFormat','-q', target+"3", '-d', '-'],stdin=PIPE) + process.communicate(input=key.encode('utf-8')) + working=True + while working: + time.sleep(0.5) + process.poll() + rc=process.returncode + if rc is None: + working=True + else: + process = None + working= False + # cryptsetup open /dev/sdb3 crypt_sdb3 + + process = Popen(['cryptsetup','luksOpen', target + "3", 'crypt_' + base_target ,'-d','-'],stdin=PIPE) + process.communicate(input=key.encode('utf-8')) + working=True + while working: + time.sleep(0.5) + process.poll() + rc=process.returncode + if rc is None: + working=True + else: + process = None + working= False + # mkfs.ext4 -L mgalive-persist /dev/mapper/crypt_sdb3 + process = Popen(['mkfs.ext4','-L', label, '/dev/mapper/crypt_' + base_target]) + process.communicate() + working=True + while working: + time.sleep(0.5) + process.poll() + rc=process.returncode + if rc is None: + working=True + else: + process = None + working= False + # cryptsetup close crypt_sdb3 + + process = Popen(['cryptsetup','luksClose', 'crypt_' + base_target ]) + process.communicate() + working=True + while working: + time.sleep(0.5) + process.poll() + rc=process.returncode + if rc is None: + working=True + else: + process = None + working= False return rc - + def __init__(self): gettext.install('isodumper', localedir='/usr/share/locale') diff --git a/lib/isodumper.py b/lib/isodumper.py index 785c996..23c506b 100755 --- a/lib/isodumper.py +++ b/lib/isodumper.py @@ -116,7 +116,7 @@ class Info(object): self.richtext=richtext self.text=text - + class IsoDumper(object): RELEASE="v1.15" @@ -212,12 +212,14 @@ class IsoDumper(object): self.progress.setDisabled() self.refreshbt.setEnabled() self.persistencecb.setDisabled() + self.cryptcb.setDisabled() + self.cryptkey.setDisabled() def onProgress(self, frac): self.logger(_('Wrote: {}% '.format(frac))) self.progress.setValue(frac) self.dialog.pollEvent() - + def raw_format(self, usb_path, fstype, label): self.operation=True if os.geteuid() > 0: @@ -310,7 +312,6 @@ class IsoDumper(object): success, message = iface.do_unmount(target) if success: #Writing step - #Dump mode iface.do_write(source, target, b) iface.get_sum(source) progress = iface.progress @@ -324,8 +325,8 @@ class IsoDumper(object): self.logger(_('Image {source} successfully written to {target}').format( source=source.split('/')[-1], target=target)) self.logger(_('Bytes written: ')+str(b)) self.progress.setLabel(_('Checking ')+target.split('/')[-1]) - self.progress.setValue(0) - self.dialog.pollEvent() + self.progress.setValue(0) + self.dialog.pollEvent() # Checking iface.check_write(target, source) progress = iface.progress @@ -340,13 +341,20 @@ class IsoDumper(object): self.dialog.pollEvent() self.logger(message) # Add persistent partition if asked - if self.persistencecb.value(): + if self.persistencecb.isChecked(): self.logger(_("Adding persistent partition")) - iface.do_persistence(target,"mgalive-persist") - self.logger(_("Added persistent partition")) + if self.cryptcb.isChecked(): + if self.cryptkey.value() == "": + self.logger(_("No key for crypted partition provided. Adding the partition aborted.")) + else: + iface.do_persistence(target,"mgalive-persist",self.cryptkey.value()) + self.logger(_("Added crypted persistent partition")) + else: + iface.do_persistence(target,"mgalive-persist", "") + self.logger(_("Added persistent partition")) #Eject self.u.eject(target) - self.success() + self.success() else: self.emergency(message) else: @@ -401,7 +409,7 @@ class IsoDumper(object): self.backupbt.setDisabled() self.refreshbt.setDisabled() self.persistencecb.setDisabled() - + def close(self): self.write_logfile() self.dialog.destroy() @@ -473,13 +481,13 @@ NTFS or ext. You can specify a volume name and the format in a new dialog box.Papoteur
Pictures : Timothée Giet"), _("A tool for writing ISO images to a device")+"
http://gitweb.mageia.org/software/isodumper", "") dlg.show(); - + def nodevDialog(self): yui.YUI.widgetFactory mgafactory = yui.YMGAWidgetFactory.getYMGAWidgetFactory(yui.YExternalWidgets.externalWidgetFactory("mga")) @@ -685,6 +702,19 @@ NTFS or ext. You can specify a volume name and the format in a new dialog box.