shellMountOperation: Support TCRYPT

This extends the ShellMountPasswordDialog by widgets which allow
specifying parameters supported by TrueCrypt and VeraCrypt compatible
volumes (TCRYPT). This includes:

 - Whether the volume to be unlocked is hidden.
 - Whether the volume to be unlocked is a system partition.
   Note: TrueCrypt and VeraCrypt only support encrypting Windows
   systems [1], so the label for this option is "Windows System Volume".
 - Whether to use a PIM [2].
 - Whether to use keyfiles. Unfortunately, GMountOperation doesn't
   support TCRYPT keyfiles, so if this checkbox is checked, we tell the
   user that they should unlock the volume with Disks, which supports
   unlocking TCRYPT volumes with keyfiles.

[1] https://www.veracrypt.fr/en/System%20Encryption.html
[2] https://www.veracrypt.fr/en/Header%20Key%20Derivation.html

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/126
This commit is contained in:
segfault
2019-04-07 23:35:07 +02:00
committed by Florian Müllner
parent af26e2b212
commit aa3e64aec3
3 changed files with 157 additions and 23 deletions

View File

@ -148,7 +148,7 @@ var ShellMountOperation = class {
}
this._dialogId = this._dialog.connect('response',
(object, choice, password, remember) => {
(object, choice, password, remember, hiddenVolume, systemVolume, pim) => {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
@ -158,6 +158,9 @@ var ShellMountOperation = class {
this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
this.mountOp.set_password(password);
this.mountOp.set_is_tcrypt_hidden_volume(hiddenVolume);
this.mountOp.set_is_tcrypt_system_volume(systemVolume);
this.mountOp.set_pim(pim);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
});
@ -286,33 +289,86 @@ var ShellMountPasswordDialog = class extends ModalDialog.ModalDialog {
let body = strings.shift() || null;
super({ styleClass: 'prompt-dialog' });
let disksApp = Shell.AppSystem.get_default().lookup_app('org.gnome.DiskUtility.desktop');
let content = new Dialog.MessageDialogContent({ icon, title, body });
this.contentLayout.add_actor(content);
content._body.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
let grid = new St.Widget({ style_class: 'prompt-dialog-grid',
layout_manager: layout });
layout.hookup_style(grid);
let rtl = grid.get_text_direction() === Clutter.TextDirection.RTL;
if (flags & Gio.AskPasswordFlags.TCRYPT) {
this._keyfilesLabel = new St.Label(({ style_class: 'prompt-dialog-keyfiles-label',
visible: false }));
this._hiddenVolume = new CheckBox.CheckBox(_("Hidden Volume"));
content.messageBox.add(this._hiddenVolume.actor);
this._systemVolume = new CheckBox.CheckBox(_("Windows System Volume"));
content.messageBox.add(this._systemVolume.actor);
this._keyfilesCheckbox = new CheckBox.CheckBox(_("Uses Keyfiles"));
this._keyfilesCheckbox.actor.connect("clicked", this._onKeyfilesCheckboxClicked.bind(this));
content.messageBox.add(this._keyfilesCheckbox.actor);
this._keyfilesLabel.clutter_text.set_markup(
/* Translators: %s is the Disks application */
_("To unlock a volume that uses keyfiles, use the <i>%s</i> utility instead.").format(disksApp.get_name())
);
this._keyfilesLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._keyfilesLabel.clutter_text.line_wrap = true;
content.messageBox.add(this._keyfilesLabel, { y_fill: false, y_align: St.Align.MIDDLE, expand: true });
this._pimLabel = new St.Label({ style_class: 'prompt-dialog-password-label',
text: _("PIM Number"),
y_align: Clutter.ActorAlign.CENTER });
this._pimEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
can_focus: true,
x_expand: true });
this._pimEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
this._pimEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
ShellEntry.addContextMenu(this._pimEntry, { isPassword: true });
if (rtl) {
layout.attach(this._pimEntry, 0, 0, 1, 1);
layout.attach(this._pimLabel, 1, 0, 1, 1);
} else {
layout.attach(this._pimLabel, 0, 0, 1, 1);
layout.attach(this._pimEntry, 1, 0, 1, 1);
}
this._pimErrorMessageLabel = new St.Label({ style_class: 'prompt-dialog-password-entry',
text: _("The PIM must be a number or empty."),
visible: false });
layout.attach(this._pimErrorMessageLabel, 0, 2, 2, 1);
} else {
this._hiddenVolume = null;
this._systemVolume = null;
this._pimEntry = null;
this._pimErrorMessageLabel = null;
}
this._passwordLabel = new St.Label({ style_class: 'prompt-dialog-password-label',
text: _("Password"),
y_align: Clutter.ActorAlign.CENTER });
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
can_focus: true,
x_expand: true});
x_expand: true });
this._passwordEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this.setInitialKeyFocus(this._passwordEntry);
if (rtl) {
layout.attach(this._passwordEntry, 0, 0, 1, 1);
layout.attach(this._passwordLabel, 1, 0, 1, 1);
layout.attach(this._passwordEntry, 0, 1, 1, 1);
layout.attach(this._passwordLabel, 1, 1, 1, 1);
} else {
layout.attach(this._passwordLabel, 0, 0, 1, 1);
layout.attach(this._passwordEntry, 1, 0, 1, 1);
layout.attach(this._passwordLabel, 0, 1, 1, 1);
layout.attach(this._passwordEntry, 1, 1, 1, 1);
}
content.messageBox.add(grid);
@ -325,8 +381,7 @@ var ShellMountPasswordDialog = class extends ModalDialog.ModalDialog {
content.messageBox.add(this._errorMessageLabel);
if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
this._rememberChoice = new CheckBox.CheckBox();
this._rememberChoice.getLabelActor().text = _("Remember Password");
this._rememberChoice = new CheckBox.CheckBox(_("Remember Password"));
this._rememberChoice.actor.checked =
global.settings.get_boolean(REMEMBER_MOUNT_PASSWORD_KEY);
content.messageBox.add(this._rememberChoice.actor);
@ -334,16 +389,26 @@ var ShellMountPasswordDialog = class extends ModalDialog.ModalDialog {
this._rememberChoice = null;
}
let buttons = [{ label: _("Cancel"),
action: this._onCancelButton.bind(this),
key: Clutter.Escape
},
{ label: _("Unlock"),
action: this._onUnlockButton.bind(this),
default: true
}];
this._defaultButtons = [{ label: _("Cancel"),
action: this._onCancelButton.bind(this),
key: Clutter.Escape
},
{ label: _("Unlock"),
action: this._onUnlockButton.bind(this),
default: true
}];
this.setButtons(buttons);
this._usesKeyfilesButtons = [{ label: _("Cancel"),
action: this._onCancelButton.bind(this),
key: Clutter.Escape
},
{ /* Translators: %s is the Disks application */
label: _("Open %s").format(disksApp.get_name()),
action: this._onOpenDisksButton.bind(this),
default: true
}];
this.setButtons(this._defaultButtons);
}
reaskPassword() {
@ -360,12 +425,58 @@ var ShellMountPasswordDialog = class extends ModalDialog.ModalDialog {
}
_onEntryActivate() {
let pim = 0;
if (this._pimEntry !== null)
pim = this._pimEntry.get_text();
if (isNaN(pim)) {
this._pimEntry.set_text('');
this._pimErrorMessageLabel.show();
return;
} else if (this._pimErrorMessageLabel !== null) {
this._pimErrorMessageLabel.hide();
}
global.settings.set_boolean(REMEMBER_MOUNT_PASSWORD_KEY,
this._rememberChoice && this._rememberChoice.actor.checked);
this.emit('response', 1,
this._passwordEntry.get_text(),
this._rememberChoice &&
this._rememberChoice.actor.checked);
this._rememberChoice.actor.checked,
this._hiddenVolume &&
this._hiddenVolume.actor.checked,
this._systemVolume &&
this._systemVolume.actor.checked,
pim);
}
_onKeyfilesCheckboxClicked() {
let useKeyfiles = this._keyfilesCheckbox.actor.checked;
this._passwordEntry.reactive = !useKeyfiles;
this._passwordEntry.can_focus = !useKeyfiles;
this._passwordEntry.clutter_text.editable = !useKeyfiles;
this._passwordEntry.clutter_text.selectable = !useKeyfiles;
this._pimEntry.reactive = !useKeyfiles;
this._pimEntry.can_focus = !useKeyfiles;
this._pimEntry.clutter_text.editable = !useKeyfiles;
this._pimEntry.clutter_text.selectable = !useKeyfiles;
this._rememberChoice.actor.reactive = !useKeyfiles;
this._rememberChoice.actor.can_focus = !useKeyfiles;
this._keyfilesLabel.visible = useKeyfiles;
this.setButtons(useKeyfiles ? this._usesKeyfilesButtons : this._defaultButtons);
}
_onOpenDisksButton() {
let app = Shell.AppSystem.get_default().lookup_app('org.gnome.DiskUtility.desktop');
if (app)
app.activate();
else
Main.notifyError(
/* Translators: %s is the Disks application */
_("Unable to start %s").format(app.get_name()),
/* Translators: %s is the Disks application */
_("Couldn't find the %s application").format(app.get_name())
);
this._onCancelButton();
}
};
@ -527,7 +638,7 @@ var GnomeShellMountOpHandler = class {
this._dialog = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags);
this._dialog.connect('response',
(object, choice, password, remember) => {
(object, choice, password, remember, hiddenVolume, systemVolume, pim) => {
let details = {};
let response;
@ -539,6 +650,9 @@ var GnomeShellMountOpHandler = class {
let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
details['password_save'] = GLib.Variant.new('u', passSave);
details['password'] = GLib.Variant.new('s', password);
details['hidden_volume'] = GLib.Variant.new('b', hiddenVolume);
details['system_volume'] = GLib.Variant.new('b', systemVolume);
details['pim'] = GLib.Variant.new('u', parseInt(pim));
}
this._clearCurrentRequest(response, details);