250 lines
11 KiB
Diff
250 lines
11 KiB
Diff
From: =?utf-8?b?Ik1hcmNvIFRyZXZpc2FuIChUcmV2acOxbyki?= <mail@3v1n0.net>
|
|
Date: Tue, 1 Mar 2022 11:57:20 +0100
|
|
Subject: gdm/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 | 129 +++++++++++++++++++++++++++++++++++++++++++++------------
|
|
1 file changed, 102 insertions(+), 27 deletions(-)
|
|
|
|
diff --git a/js/gdm/util.js b/js/gdm/util.js
|
|
index 89ad544..3697ecd 100644
|
|
--- a/js/gdm/util.js
|
|
+++ b/js/gdm/util.js
|
|
@@ -43,6 +43,7 @@ export const DISABLE_USER_LIST_KEY = 'disable-user-list';
|
|
|
|
// Give user 48ms to read each character of a PAM message
|
|
const USER_READ_TIME = 48;
|
|
+const FINGERPRINT_SERVICE_PROXY_TIMEOUT = 5000;
|
|
const FINGERPRINT_ERROR_TIMEOUT_WAIT = 15;
|
|
|
|
/**
|
|
@@ -108,16 +109,50 @@ export class ShellUserVerifier 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._fprintManager = new FprintManagerProxy(Gio.DBus.system,
|
|
- 'net.reactivated.Fprint',
|
|
- '/net/reactivated/Fprint/Manager',
|
|
- null,
|
|
- null,
|
|
- Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
|
|
+ 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._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
|
|
@@ -136,6 +171,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
|
|
this.reauthenticating = false;
|
|
|
|
this._failCounter = 0;
|
|
+ this._startedServices = new Set();
|
|
this._unavailableServices = new Set();
|
|
|
|
this._credentialManagers = {};
|
|
@@ -225,6 +261,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
|
|
|
|
this._clearUserVerifier();
|
|
this._clearMessageQueue();
|
|
+ this._startedServices.clear();
|
|
}
|
|
|
|
destroy() {
|
|
@@ -345,27 +382,52 @@ export class ShellUserVerifier 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) {
|
|
@@ -466,6 +528,7 @@ export class ShellUserVerifier 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),
|
|
@@ -519,6 +582,10 @@ export class ShellUserVerifier 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');
|
|
@@ -557,11 +624,14 @@ export class ShellUserVerifier 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) {
|
|
@@ -654,8 +724,9 @@ export class ShellUserVerifier 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');
|
|
}
|
|
@@ -736,6 +807,10 @@ export class ShellUserVerifier 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
|