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:
parent
af26e2b212
commit
aa3e64aec3
@ -382,7 +382,6 @@ StScrollBar {
|
|||||||
|
|
||||||
.prompt-dialog-password-box {
|
.prompt-dialog-password-box {
|
||||||
spacing: 1em;
|
spacing: 1em;
|
||||||
padding-bottom: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.prompt-dialog-error-label {
|
.prompt-dialog-error-label {
|
||||||
@ -405,11 +404,23 @@ StScrollBar {
|
|||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prompt-dialog-pim-box {
|
||||||
|
spacing: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.prompt-dialog-grid {
|
.prompt-dialog-grid {
|
||||||
spacing-rows: 15px;
|
spacing-rows: 15px;
|
||||||
spacing-columns: 1em;
|
spacing-columns: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prompt-dialog-keyfiles-box {
|
||||||
|
spacing: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-dialog-button.button {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Polkit Dialog */
|
/* Polkit Dialog */
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ const Mainloop = imports.mainloop;
|
|||||||
const Params = imports.misc.params;
|
const Params = imports.misc.params;
|
||||||
|
|
||||||
const GnomeSession = imports.misc.gnomeSession;
|
const GnomeSession = imports.misc.gnomeSession;
|
||||||
|
const Main = imports.ui.main;
|
||||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||||
|
|
||||||
var GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
|
var GNOME_SESSION_AUTOMOUNT_INHIBIT = 16;
|
||||||
@ -199,12 +200,20 @@ var AutomountManager = class {
|
|||||||
// error strings are not unique for the cases in the comments below.
|
// error strings are not unique for the cases in the comments below.
|
||||||
if (e.message.includes('No key available with this passphrase') || // cryptsetup
|
if (e.message.includes('No key available with this passphrase') || // cryptsetup
|
||||||
e.message.includes('No key available to unlock device') || // udisks (no password)
|
e.message.includes('No key available to unlock device') || // udisks (no password)
|
||||||
e.message.includes('Error unlocking')) { // udisks (wrong password)
|
// libblockdev wrong password opening LUKS device
|
||||||
|
e.message.includes('Failed to activate device: Incorrect passphrase') ||
|
||||||
|
// cryptsetup returns EINVAL in many cases, including wrong TCRYPT password/parameters
|
||||||
|
e.message.includes('Failed to load device\'s parameters: Invalid argument')) {
|
||||||
|
|
||||||
this._reaskPassword(volume);
|
this._reaskPassword(volume);
|
||||||
} else {
|
} else {
|
||||||
|
if (e.message.includes('Compiled against a version of libcryptsetup that does not support the VeraCrypt PIM setting')) {
|
||||||
|
Main.notifyError(_("Unable to unlock volume"),
|
||||||
|
_("The installed udisks version does not support the PIM setting"));
|
||||||
|
}
|
||||||
|
|
||||||
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
|
||||||
log('Unable to mount volume ' + volume.get_name() + ': ' + e.toString());
|
log('Unable to mount volume ' + volume.get_name() + ': ' + e.toString());
|
||||||
|
|
||||||
this._closeOperation(volume);
|
this._closeOperation(volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ var ShellMountOperation = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._dialogId = this._dialog.connect('response',
|
this._dialogId = this._dialog.connect('response',
|
||||||
(object, choice, password, remember) => {
|
(object, choice, password, remember, hiddenVolume, systemVolume, pim) => {
|
||||||
if (choice == -1) {
|
if (choice == -1) {
|
||||||
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
|
||||||
} else {
|
} else {
|
||||||
@ -158,6 +158,9 @@ var ShellMountOperation = class {
|
|||||||
this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
|
this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
|
||||||
|
|
||||||
this.mountOp.set_password(password);
|
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);
|
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -286,33 +289,86 @@ var ShellMountPasswordDialog = class extends ModalDialog.ModalDialog {
|
|||||||
let body = strings.shift() || null;
|
let body = strings.shift() || null;
|
||||||
super({ styleClass: 'prompt-dialog' });
|
super({ styleClass: 'prompt-dialog' });
|
||||||
|
|
||||||
|
let disksApp = Shell.AppSystem.get_default().lookup_app('org.gnome.DiskUtility.desktop');
|
||||||
|
|
||||||
let content = new Dialog.MessageDialogContent({ icon, title, body });
|
let content = new Dialog.MessageDialogContent({ icon, title, body });
|
||||||
this.contentLayout.add_actor(content);
|
this.contentLayout.add_actor(content);
|
||||||
|
content._body.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||||||
|
|
||||||
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
|
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
|
||||||
let grid = new St.Widget({ style_class: 'prompt-dialog-grid',
|
let grid = new St.Widget({ style_class: 'prompt-dialog-grid',
|
||||||
layout_manager: layout });
|
layout_manager: layout });
|
||||||
layout.hookup_style(grid);
|
layout.hookup_style(grid);
|
||||||
|
|
||||||
let rtl = grid.get_text_direction() === Clutter.TextDirection.RTL;
|
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',
|
this._passwordLabel = new St.Label({ style_class: 'prompt-dialog-password-label',
|
||||||
text: _("Password"),
|
text: _("Password"),
|
||||||
y_align: Clutter.ActorAlign.CENTER });
|
y_align: Clutter.ActorAlign.CENTER });
|
||||||
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
|
||||||
can_focus: true,
|
can_focus: true,
|
||||||
x_expand: true});
|
x_expand: true });
|
||||||
this._passwordEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
|
this._passwordEntry.clutter_text.connect('activate', this._onEntryActivate.bind(this));
|
||||||
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
|
||||||
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
|
||||||
this.setInitialKeyFocus(this._passwordEntry);
|
this.setInitialKeyFocus(this._passwordEntry);
|
||||||
|
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
layout.attach(this._passwordEntry, 0, 0, 1, 1);
|
layout.attach(this._passwordEntry, 0, 1, 1, 1);
|
||||||
layout.attach(this._passwordLabel, 1, 0, 1, 1);
|
layout.attach(this._passwordLabel, 1, 1, 1, 1);
|
||||||
} else {
|
} else {
|
||||||
layout.attach(this._passwordLabel, 0, 0, 1, 1);
|
layout.attach(this._passwordLabel, 0, 1, 1, 1);
|
||||||
layout.attach(this._passwordEntry, 1, 0, 1, 1);
|
layout.attach(this._passwordEntry, 1, 1, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
content.messageBox.add(grid);
|
content.messageBox.add(grid);
|
||||||
@ -325,8 +381,7 @@ var ShellMountPasswordDialog = class extends ModalDialog.ModalDialog {
|
|||||||
content.messageBox.add(this._errorMessageLabel);
|
content.messageBox.add(this._errorMessageLabel);
|
||||||
|
|
||||||
if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
|
if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
|
||||||
this._rememberChoice = new CheckBox.CheckBox();
|
this._rememberChoice = new CheckBox.CheckBox(_("Remember Password"));
|
||||||
this._rememberChoice.getLabelActor().text = _("Remember Password");
|
|
||||||
this._rememberChoice.actor.checked =
|
this._rememberChoice.actor.checked =
|
||||||
global.settings.get_boolean(REMEMBER_MOUNT_PASSWORD_KEY);
|
global.settings.get_boolean(REMEMBER_MOUNT_PASSWORD_KEY);
|
||||||
content.messageBox.add(this._rememberChoice.actor);
|
content.messageBox.add(this._rememberChoice.actor);
|
||||||
@ -334,16 +389,26 @@ var ShellMountPasswordDialog = class extends ModalDialog.ModalDialog {
|
|||||||
this._rememberChoice = null;
|
this._rememberChoice = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttons = [{ label: _("Cancel"),
|
this._defaultButtons = [{ label: _("Cancel"),
|
||||||
action: this._onCancelButton.bind(this),
|
action: this._onCancelButton.bind(this),
|
||||||
key: Clutter.Escape
|
key: Clutter.Escape
|
||||||
},
|
},
|
||||||
{ label: _("Unlock"),
|
{ label: _("Unlock"),
|
||||||
action: this._onUnlockButton.bind(this),
|
action: this._onUnlockButton.bind(this),
|
||||||
default: true
|
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() {
|
reaskPassword() {
|
||||||
@ -360,12 +425,58 @@ var ShellMountPasswordDialog = class extends ModalDialog.ModalDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onEntryActivate() {
|
_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,
|
global.settings.set_boolean(REMEMBER_MOUNT_PASSWORD_KEY,
|
||||||
this._rememberChoice && this._rememberChoice.actor.checked);
|
this._rememberChoice && this._rememberChoice.actor.checked);
|
||||||
this.emit('response', 1,
|
this.emit('response', 1,
|
||||||
this._passwordEntry.get_text(),
|
this._passwordEntry.get_text(),
|
||||||
this._rememberChoice &&
|
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 = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags);
|
||||||
this._dialog.connect('response',
|
this._dialog.connect('response',
|
||||||
(object, choice, password, remember) => {
|
(object, choice, password, remember, hiddenVolume, systemVolume, pim) => {
|
||||||
let details = {};
|
let details = {};
|
||||||
let response;
|
let response;
|
||||||
|
|
||||||
@ -539,6 +650,9 @@ var GnomeShellMountOpHandler = class {
|
|||||||
let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
|
let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
|
||||||
details['password_save'] = GLib.Variant.new('u', passSave);
|
details['password_save'] = GLib.Variant.new('u', passSave);
|
||||||
details['password'] = GLib.Variant.new('s', password);
|
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);
|
this._clearCurrentRequest(response, details);
|
||||||
|
Loading…
Reference in New Issue
Block a user