mutter/debian/patches/gdm-util-Only-start-fingerprint-service-synchronously-whe.patch

249 lines
11 KiB
Diff
Raw Normal View History

2023-06-26 23:18:37 +02:00
From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
Date: Tue, 1 Mar 2022 11:57:20 +0100
Subject: gmd/util: Only start fingerprint service synchronously when it's
default
On ShellUserVerifier construction we used to start fprintd in a sync
fashion all the times, however in case the daemon had startup failures
or was hanging for whatever reason (like due to devices probing, given
that fprintd synchronously wait for them all to be initialized) we used
to just fail, leaving gdm or the lockscreen inusable.
While this could be prevented with a try/catch statement, there's no
much point to wait for fprintd if that's not the default authentication
service, and so:
- If we use gdm-fingerprint as default auth method, use a sync call to
initialize it and in case of failures, just continue with fallback
authentication mechanism (password)
- Otherwise, asynchronously initialize fprintd and continue with the
ShellUserVerifier without fingerprint support until we got a reply.
In case the service fails to deliver us a result, we don't give up
but we will try doing that at each authentication via
_checkForFingerprintReader().
In case all works properly, as per the previous commit, once the
initialization is done, we'll start the fingerprint PAM gdm service.
Fixes #5168
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/gnome-shell/+bug/1962566
Bug-GNOME: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5168
Origin: https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2267
---
js/gdm/util.js | 128 +++++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 101 insertions(+), 27 deletions(-)
diff --git a/js/gdm/util.js b/js/gdm/util.js
index 78c45cb..e4902f8 100644
--- a/js/gdm/util.js
+++ b/js/gdm/util.js
@@ -44,6 +44,7 @@ var DISABLE_USER_LIST_KEY = 'disable-user-list';
// Give user 48ms to read each character of a PAM message
var USER_READ_TIME = 48;
+const FINGERPRINT_SERVICE_PROXY_TIMEOUT = 5000;
const FINGERPRINT_ERROR_TIMEOUT_WAIT = 15;
var MessageType = {
@@ -148,16 +149,49 @@ var ShellUserVerifier = class extends Signals.EventEmitter {
this._preemptingService = null;
this._settings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA });
- this._settings.connect('changed',
- this._updateDefaultService.bind(this));
- this._updateDefaultService();
+ this._settings.connect('changed', () => this._updateDefaultServiceWithFallback());
+
+ this._fingerprintReaderType = FingerprintReaderType.NONE;
+ if (this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) {
+ const fprintManager = new FprintManagerProxy(Gio.DBus.system,
+ 'net.reactivated.Fprint',
+ '/net/reactivated/Fprint/Manager',
+ null,
+ null,
+ Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES |
+ Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION |
+ Gio.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS);
+
+ // Do not wait too much for fprintd to reply, as in case it hangs
+ // we should fail early without having the shell to misbehave because
+ fprintManager.set_default_timeout(FINGERPRINT_SERVICE_PROXY_TIMEOUT);
- this._fprintManager = new FprintManagerProxy(Gio.DBus.system,
- 'net.reactivated.Fprint',
- '/net/reactivated/Fprint/Manager',
- null,
- null,
- Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
+ this._updateDefaultService();
+
+ if (!this._defaultService) {
+ // Fingerprint is the default one, we must wait for it!
+ try {
+ const [devicePath] = fprintManager.GetDefaultDeviceSync();
+ this._fprintManager = fprintManager;
+
+ const fprintDeviceProxy = new FprintDeviceProxy(Gio.DBus.system,
+ 'net.reactivated.Fprint', devicePath, null, null,
+ Gio.DBusProxyFlags.NOT_CONNECT_SIGNALS);
+ this._setFingerprintReaderType(fprintDeviceProxy['scan-type']);
+ } catch (e) {
+ logError(e, 'Failed to initialize fprintd service');
+ } finally {
+ this._updateDefaultServiceWithFallback();
+ }
+ } else {
+ // Ensure fingerprint service starts, but do not wait for it
+ this._updateFingerprintReaderType(fprintManager, null).then(
+ () => (this._fprintManager = fprintManager)).catch(
+ e => logError(e, 'Failed to initialize fprintd service'));
+ }
+ } else {
+ this._updateDefaultServiceWithFallback();
+ }
this._smartcardManager = SmartcardManager.getSmartcardManager();
// We check for smartcards right away, since an inserted smartcard
@@ -176,6 +210,7 @@ var ShellUserVerifier = class extends Signals.EventEmitter {
this.reauthenticating = false;
this._failCounter = 0;
+ this._startedServices = new Set();
this._unavailableServices = new Set();
this._credentialManagers = {};
@@ -265,6 +300,7 @@ var ShellUserVerifier = class extends Signals.EventEmitter {
this._clearUserVerifier();
this._clearMessageQueue();
+ this._startedServices.clear();
}
destroy() {
@@ -385,27 +421,52 @@ var ShellUserVerifier = class extends Signals.EventEmitter {
}
async _checkForFingerprintReader() {
- this._fingerprintReaderType = FingerprintReaderType.NONE;
-
- if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY) ||
- this._fprintManager == null) {
- this._updateDefaultService();
+ if (!this._fprintManager) {
+ this._updateDefaultServiceWithFallback();
return;
}
+ if (this._fingerprintReaderType !== FingerprintReaderType.NONE)
+ return;
+
+ await this._updateFingerprintReaderType(this._fprintManager, this._cancellable);
+ }
+
+ async _updateFingerprintReaderType(fprintManager, cancellable) {
try {
- const [device] = await this._fprintManager.GetDefaultDeviceAsync(
- Gio.DBusCallFlags.NONE, this._cancellable);
- const fprintDeviceProxy = new FprintDeviceProxy(Gio.DBus.system,
- 'net.reactivated.Fprint',
- device);
- const fprintDeviceType = fprintDeviceProxy['scan-type'];
+ // Wrappers don't support null cancellable, so let's cheat about it
+ const [devicePath] = await fprintManager.GetDefaultDeviceAsync(
+ cancellable ? cancellable : Gio.DBusCallFlags.NONE);
+ const fprintDeviceProxy = await new Promise((resolve, reject) => {
+ const proxy = new FprintDeviceProxy(Gio.DBus.system,
+ 'net.reactivated.Fprint', devicePath, (_, error) => {
+ if (error)
+ reject(error);
+ else
+ resolve(proxy);
+ }, cancellable, Gio.DBusProxyFlags.NOT_CONNECT_SIGNALS);
+ });
+ this._setFingerprintReaderType(fprintDeviceProxy['scan-type']);
+ this._updateDefaultServiceWithFallback();
+
+ if (this._userVerifier &&
+ !this._startedServices.has(FINGERPRINT_SERVICE_NAME)) {
+ if (!this._hold?.isAcquired())
+ this._hold = new Batch.Hold();
+ await this._maybeStartFingerprintVerification();
+ }
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ }
+ }
- this._fingerprintReaderType = fprintDeviceType === 'swipe'
- ? FingerprintReaderType.SWIPE
- : FingerprintReaderType.PRESS;
- this._updateDefaultService();
- } catch (e) {}
+ _setFingerprintReaderType(fprintDeviceType) {
+ this._fingerprintReaderType =
+ FingerprintReaderType[fprintDeviceType.toUpperCase()];
+
+ if (this._fingerprintReaderType === undefined)
+ throw new Error(`Unexpected fingerprint device type '${fprintDeviceType}'`);
}
_onCredentialManagerAuthenticated(credentialManager, _token) {
@@ -506,6 +567,7 @@ var ShellUserVerifier = class extends Signals.EventEmitter {
'problem', this._onProblem.bind(this),
'info-query', this._onInfoQuery.bind(this),
'secret-info-query', this._onSecretInfoQuery.bind(this),
+ 'conversation-started', this._onConversationStarted.bind(this),
'conversation-stopped', this._onConversationStopped.bind(this),
'service-unavailable', this._onServiceUnavailable.bind(this),
'reset', this._onReset.bind(this),
@@ -559,6 +621,10 @@ var ShellUserVerifier = class extends Signals.EventEmitter {
this._defaultService = SMARTCARD_SERVICE_NAME;
else if (this._fingerprintReaderType !== FingerprintReaderType.NONE)
this._defaultService = FINGERPRINT_SERVICE_NAME;
+ }
+
+ _updateDefaultServiceWithFallback() {
+ this._updateDefaultService();
if (!this._defaultService) {
log("no authentication service is enabled, using password authentication");
@@ -597,11 +663,14 @@ var ShellUserVerifier = class extends Signals.EventEmitter {
_beginVerification() {
this._startService(this._getForegroundService());
+ this._maybeStartFingerprintVerification();
+ }
+ async _maybeStartFingerprintVerification() {
if (this._userName &&
this._fingerprintReaderType !== FingerprintReaderType.NONE &&
!this.serviceIsForeground(FINGERPRINT_SERVICE_NAME))
- this._startService(FINGERPRINT_SERVICE_NAME);
+ await this._startService(FINGERPRINT_SERVICE_NAME);
}
_onChoiceListQuery(client, serviceName, promptMessage, list) {
@@ -694,8 +763,9 @@ var ShellUserVerifier = class extends Signals.EventEmitter {
_onReset() {
// Clear previous attempts to authenticate
this._failCounter = 0;
+ this._startedServices.clear();
this._unavailableServices.clear();
- this._updateDefaultService();
+ this._updateDefaultServiceWithFallback();
this.emit('reset');
}
@@ -776,6 +846,10 @@ var ShellUserVerifier = class extends Signals.EventEmitter {
this._queueMessage(serviceName, errorMessage, MessageType.ERROR);
}
+ _onConversationStarted(client, serviceName) {
+ this._startedServices.add(serviceName);
+ }
+
_onConversationStopped(client, serviceName) {
// If the login failed with the preauthenticated oVirt credentials
// then discard the credentials and revert to default authentication