From fcbc915ff470473bca6680b569e86dc84dc4fe4e Mon Sep 17 00:00:00 2001 From: Joan Torres Lopez Date: Mon, 9 Mar 2026 15:46:17 +0100 Subject: [PATCH] Fix to automatically start fingerprint when enabled Resolves: RHEL-4166 --- ...y-initialize-all-internal-properties.patch | 1008 +++++++++++++++++ gnome-shell.spec | 9 +- 2 files changed, 1015 insertions(+), 2 deletions(-) create mode 100644 0001-gdm-util-Early-initialize-all-internal-properties.patch diff --git a/0001-gdm-util-Early-initialize-all-internal-properties.patch b/0001-gdm-util-Early-initialize-all-internal-properties.patch new file mode 100644 index 0000000..de3eb38 --- /dev/null +++ b/0001-gdm-util-Early-initialize-all-internal-properties.patch @@ -0,0 +1,1008 @@ +From f1c0f65075b839b59e082d1cc060f9bc80ac8574 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 21 Oct 2022 19:09:09 +0200 +Subject: [PATCH 01/13] gdm/util: Early initialize all internal properties + +These may be used later by methods called by constructors, so ensure +that they're all defined early. + +Part-of: +--- + js/gdm/util.js | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index 3f32740..ecc05fa 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -142,10 +142,22 @@ var ShellUserVerifier = class { + this._reauthOnly = params.reauthenticationOnly; + + this._client = client; ++ this._cancellable = null; + + this._defaultService = null; + this._preemptingService = null; + ++ this._messageQueue = []; ++ this._messageQueueTimeoutId = 0; ++ ++ this._failCounter = 0; ++ this._unavailableServices = new Set(); ++ ++ this._credentialManagers = {}; ++ ++ this.reauthenticating = false; ++ this.smartcardDetected = false; ++ + this._settings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA }); + this._settings.connect('changed', + this._updateDefaultService.bind(this)); +@@ -172,14 +184,6 @@ var ShellUserVerifier = class { + this._smartcardRemovedId = this._smartcardManager.connect('smartcard-removed', + this._checkForSmartcard.bind(this)); + +- this._messageQueue = []; +- this._messageQueueTimeoutId = 0; +- this.reauthenticating = false; +- +- this._failCounter = 0; +- this._unavailableServices = new Set(); +- +- this._credentialManagers = {}; + this._credentialManagers[OVirt.SERVICE_NAME] = OVirt.getOVirtCredentialsManager(); + this._credentialManagers[Vmware.SERVICE_NAME] = Vmware.getVmwareCredentialsManager(); + +-- +2.51.0 + + +From 9af029e968a6ad369ae8f73fb0e7d73f37ebe677 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 21 Oct 2022 17:47:15 +0200 +Subject: [PATCH 02/13] gdm/util: Keep track of started services that are + currently active + +If we have a late activation of a service backend we may need to check +whether it has been already started, and in case it has not, we can try +loading it. + +So rely on gdm to see what service has been started, instead of handling +it manually on our side only. + +Part-of: +--- + js/gdm/util.js | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index ecc05fa..7694b6f 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -151,6 +151,7 @@ var ShellUserVerifier = class { + this._messageQueueTimeoutId = 0; + + this._failCounter = 0; ++ this._activeServices = new Set(); + this._unavailableServices = new Set(); + + this._credentialManagers = {}; +@@ -257,6 +258,7 @@ var ShellUserVerifier = class { + + this._clearUserVerifier(); + this._clearMessageQueue(); ++ this._activeServices.clear(); + } + + destroy() { +@@ -513,6 +515,8 @@ var ShellUserVerifier = class { + this._signalIds.push(id); + id = this._userVerifier.connect('secret-info-query', this._onSecretInfoQuery.bind(this)); + this._signalIds.push(id); ++ id = this._userVerifier.connect('conversation-started', this._onConversationStarted.bind(this)); ++ this._signalIds.push(id); + id = this._userVerifier.connect('conversation-stopped', this._onConversationStopped.bind(this)); + this._signalIds.push(id); + id = this._userVerifier.connect('service-unavailable', this._onServiceUnavailable.bind(this)); +@@ -573,6 +577,7 @@ var ShellUserVerifier = class { + async _startService(serviceName) { + this._hold.acquire(); + try { ++ this._activeServices.add(serviceName); + if (this._userName) { + await this._userVerifier.call_begin_verification_for_user( + serviceName, this._userName, this._cancellable); +@@ -581,6 +586,7 @@ var ShellUserVerifier = class { + serviceName, this._cancellable); + } + } catch (e) { ++ this._activeServices.delete(serviceName); + if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + return; + if (!this.serviceIsForeground(serviceName)) { +@@ -696,6 +702,7 @@ var ShellUserVerifier = class { + _onReset() { + // Clear previous attempts to authenticate + this._failCounter = 0; ++ this._activeServices.clear(); + this._unavailableServices.clear(); + this._updateDefaultService(); + +@@ -776,7 +783,13 @@ var ShellUserVerifier = class { + this._queueMessage(serviceName, errorMessage, MessageType.ERROR); + } + ++ _onConversationStarted(client, serviceName) { ++ this._activeServices.add(serviceName); ++ } ++ + _onConversationStopped(client, serviceName) { ++ this._activeServices.delete(serviceName); ++ + // If the login failed with the preauthenticated oVirt credentials + // then discard the credentials and revert to default authentication + // mechanism. +-- +2.51.0 + + +From dc4f0f9053b0db3d064be5e8e74699a3cd11209f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Fri, 21 Oct 2022 19:05:04 +0200 +Subject: [PATCH 03/13] gdm/util: Restart auth if default service changed + +If a default service changed while the previous one was active we need to +reset the authentication so that we set back to the expected one. + +Part-of: +--- + js/gdm/util.js | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index e3c6e1b..83c7d43 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -559,6 +559,8 @@ var ShellUserVerifier = class { + } + + _updateDefaultService() { ++ const oldDefaultService = this._defaultService; ++ + if (this._smartcardManager?.loggedInWithToken()) + this._defaultService = SMARTCARD_SERVICE_NAME; + else if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY)) +@@ -572,6 +574,11 @@ var ShellUserVerifier = class { + log("no authentication service is enabled, using password authentication"); + this._defaultService = PASSWORD_SERVICE_NAME; + } ++ ++ if (oldDefaultService && ++ oldDefaultService !== this._defaultService && ++ this._activeServices.has(oldDefaultService)) ++ this._cancelAndReset(); + } + + async _startService(serviceName) { +-- +2.51.0 + + +From 9f3a9f8f2c619e9b9f49e818434dc6569eea66d9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Tue, 25 Oct 2022 03:51:30 +0200 +Subject: [PATCH 04/13] gdm/util: Do not use gjs GDBus proxy wrapper for fprint + manager + +It only adds more complexity, while we can handle it all manually quite +easier now. + +Part-of: +--- + js/gdm/util.js | 19 +++++++++++-------- + 1 file changed, 11 insertions(+), 8 deletions(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index 7c5a084..7babc26 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -13,8 +13,8 @@ const { loadInterfaceXML } = imports.misc.fileUtils; + const Params = imports.misc.params; + const SmartcardManager = imports.misc.smartcardManager; + +-const FprintManagerIface = loadInterfaceXML('net.reactivated.Fprint.Manager'); +-const FprintManagerProxy = Gio.DBusProxy.makeProxyWrapper(FprintManagerIface); ++const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml( ++ loadInterfaceXML('net.reactivated.Fprint.Manager')); + const FprintDeviceIface = loadInterfaceXML('net.reactivated.Fprint.Device'); + const FprintDeviceProxy = Gio.DBusProxy.makeProxyWrapper(FprintDeviceIface); + +@@ -163,12 +163,15 @@ var ShellUserVerifier = class { + this._settings.connect('changed', + this._updateDefaultService.bind(this)); + +- this._fprintManager = new FprintManagerProxy(Gio.DBus.system, +- 'net.reactivated.Fprint', +- '/net/reactivated/Fprint/Manager', +- null, +- null, +- Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES); ++ this._fprintManager = new Gio.DBusProxy({ ++ g_connection: Gio.DBus.system, ++ g_name: 'net.reactivated.Fprint', ++ g_object_path: '/net/reactivated/Fprint/Manager', ++ g_interface_name: FprintManagerInfo.name, ++ g_interface_info: FprintManagerInfo, ++ g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, ++ }); ++ this._fprintManager.init(null); + this._smartcardManager = SmartcardManager.getSmartcardManager(); + + // We check for smartcards right away, since an inserted smartcard +-- +2.51.0 + + +From ea3731de3828827bb632ab7eba64cf4d0e85abc9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Tue, 1 Mar 2022 11:57:20 +0100 +Subject: [PATCH 05/13] gdm/util: Only initialize fingerprint and smart card + managers if needed + +There's no point to initialize the fprint proxy and the smartcard +manager if they are disabled in authentication settings, so just avoid +initializing them, but at the same time this implies tracking of user +changes and so: + - If a new service has been enabled, we initialize it + - If a service has been disabled we destroy it and reset the + authentication if such service was currently active + +In both cases we do update the default service. + +Part-of: +--- + js/gdm/util.js | 100 +++++++++++++++++++++++++++++++++++-------------- + 1 file changed, 72 insertions(+), 28 deletions(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index 7babc26..f2041cb 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -160,34 +160,10 @@ var ShellUserVerifier = class { + this.smartcardDetected = false; + + this._settings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA }); +- this._settings.connect('changed', +- this._updateDefaultService.bind(this)); +- +- this._fprintManager = new Gio.DBusProxy({ +- g_connection: Gio.DBus.system, +- g_name: 'net.reactivated.Fprint', +- g_object_path: '/net/reactivated/Fprint/Manager', +- g_interface_name: FprintManagerInfo.name, +- g_interface_info: FprintManagerInfo, +- g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, +- }); +- this._fprintManager.init(null); +- this._smartcardManager = SmartcardManager.getSmartcardManager(); +- +- // We check for smartcards right away, since an inserted smartcard +- // at startup should result in immediately initiating authentication. +- // This is different than fingerprint readers, where we only check them +- // after a user has been picked. +- this.smartcardDetected = false; +- this._checkForSmartcard(); +- ++ this._settings.connect('changed', () => this._onSettingsChanged()); ++ this._updateEnabledServices(); + this._updateDefaultService(); + +- this._smartcardInsertedId = this._smartcardManager.connect('smartcard-inserted', +- this._checkForSmartcard.bind(this)); +- this._smartcardRemovedId = this._smartcardManager.connect('smartcard-removed', +- this._checkForSmartcard.bind(this)); +- + this._credentialManagers[OVirt.SERVICE_NAME] = OVirt.getOVirtCredentialsManager(); + this._credentialManagers[Vmware.SERVICE_NAME] = Vmware.getVmwareCredentialsManager(); + +@@ -270,10 +246,12 @@ var ShellUserVerifier = class { + this._settings.run_dispose(); + this._settings = null; + +- this._smartcardManager.disconnect(this._smartcardInsertedId); +- this._smartcardManager.disconnect(this._smartcardRemovedId); ++ this._smartcardManager?.disconnect(this._smartcardInsertedId); ++ this._smartcardManager?.disconnect(this._smartcardRemovedId); + this._smartcardManager = null; + ++ this._fingerprintManager = null; ++ + for (let service in this._credentialManagers) { + let credentialManager = this._credentialManagers[service]; + credentialManager.disconnect(credentialManager._authenticatedSignalId); +@@ -388,11 +366,25 @@ var ShellUserVerifier = class { + this.emit('show-message', null, null, MessageType.NONE); + } + ++ _initFingerprintManager() { ++ if (this._fprintManager) ++ return; ++ ++ this._fprintManager = new Gio.DBusProxy({ ++ g_connection: Gio.DBus.system, ++ g_name: 'net.reactivated.Fprint', ++ g_object_path: '/net/reactivated/Fprint/Manager', ++ g_interface_name: FprintManagerInfo.name, ++ g_interface_info: FprintManagerInfo, ++ g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, ++ }); ++ this._fprintManager.init(null); ++ } ++ + _checkForFingerprintReader() { + this._fingerprintReaderType = FingerprintReaderType.NONE; + +- if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY) || +- this._fprintManager == null) { ++ if (!this._fprintManager) { + this._updateDefaultService(); + return; + } +@@ -419,6 +411,25 @@ var ShellUserVerifier = class { + this.emit('credential-manager-authenticated'); + } + ++ _initSmartcardManager() { ++ if (this._smartcardManager) ++ return; ++ ++ this._smartcardManager = SmartcardManager.getSmartcardManager(); ++ ++ // We check for smartcards right away, since an inserted smartcard ++ // at startup should result in immediately initiating authentication. ++ // This is different than fingerprint readers, where we only check them ++ // after a user has been picked. ++ this.smartcardDetected = false; ++ this._checkForSmartcard(); ++ ++ this._smartcardInsertedId = this._smartcardManager.connect('smartcard-inserted', ++ this._checkForSmartcard.bind(this)); ++ this._smartcardRemovedId = this._smartcardManager.connect('smartcard-removed', ++ this._checkForSmartcard.bind(this)); ++ } ++ + _checkForSmartcard() { + let smartcardDetected; + +@@ -561,6 +572,39 @@ var ShellUserVerifier = class { + serviceName === FINGERPRINT_SERVICE_NAME; + } + ++ _onSettingsChanged() { ++ this._updateEnabledServices(); ++ this._updateDefaultService(); ++ } ++ ++ _updateEnabledServices() { ++ let needsReset = false; ++ ++ if (this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) { ++ this._initFingerprintManager(); ++ } else if (this._fingerprintManager) { ++ this._fingerprintManager = null; ++ this._fingerprintReaderType = FingerprintReaderType.NONE; ++ ++ if (this._activeServices.has(FINGERPRINT_SERVICE_NAME)) ++ needsReset = true; ++ } ++ ++ if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY)) { ++ this._initSmartcardManager(); ++ } else if (this._smartcardManager) { ++ this._smartcardManager.disconnect(this._smartcardInsertedId); ++ this._smartcardManager.disconnect(this._smartcardRemovedId); ++ this._smartcardManager = null; ++ ++ if (this._activeServices.has(SMARTCARD_SERVICE_NAME)) ++ needsReset = true; ++ } ++ ++ if (needsReset) ++ this._cancelAndReset(); ++ } ++ + _updateDefaultService() { + const oldDefaultService = this._defaultService; + +@@ -568,7 +612,7 @@ var ShellUserVerifier = class { + this._defaultService = SMARTCARD_SERVICE_NAME; + else if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY)) + this._defaultService = PASSWORD_SERVICE_NAME; +- else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY)) ++ else if (this._smartcardManager) + this._defaultService = SMARTCARD_SERVICE_NAME; + else if (this._fingerprintReaderType !== FingerprintReaderType.NONE) + this._defaultService = FINGERPRINT_SERVICE_NAME; +-- +2.51.0 + + +From 0d24563296a5a53712fe72c8995a1322fd5b4121 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Tue, 25 Oct 2022 04:47:08 +0200 +Subject: [PATCH 06/13] gdm/util: Do not handle fingerprint async errors in + call + +It's better to do this at caller level so that we have more control of +what to do on errors. + +Part-of: +--- + js/gdm/util.js | 26 ++++++++++++-------------- + 1 file changed, 12 insertions(+), 14 deletions(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index f2041cb..569f3fd 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -197,7 +197,7 @@ var ShellUserVerifier = class { + this._userName = userName; + this.reauthenticating = false; + +- this._checkForFingerprintReader(); ++ this._checkForFingerprintReader().catch(logError); + + // If possible, reauthenticate an already running session, + // so any session specific credentials get updated appropriately +@@ -381,7 +381,7 @@ var ShellUserVerifier = class { + this._fprintManager.init(null); + } + +- _checkForFingerprintReader() { ++ async _checkForFingerprintReader() { + this._fingerprintReaderType = FingerprintReaderType.NONE; + + if (!this._fprintManager) { +@@ -389,21 +389,17 @@ var ShellUserVerifier = class { + return; + } + +- this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, +- (params, error) => { +- if (!error && params) { +- const [device] = params; +- const fprintDeviceProxy = new FprintDeviceProxy(Gio.DBus.system, +- 'net.reactivated.Fprint', +- device); +- const fprintDeviceType = fprintDeviceProxy['scan-type']; +- +- this._fingerprintReaderType = fprintDeviceType === 'swipe' +- ? FingerprintReaderType.SWIPE +- : FingerprintReaderType.PRESS; +- this._updateDefaultService(); +- } +- }); ++ 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']; ++ ++ this._fingerprintReaderType = fprintDeviceType === 'swipe' ++ ? FingerprintReaderType.SWIPE ++ : FingerprintReaderType.PRESS; ++ this._updateDefaultService(); + } + + _onCredentialManagerAuthenticated(credentialManager, _token) { +-- +2.51.0 + + +From d5ca108a65488c369929cfc360b1532e953c7286 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Tue, 12 Apr 2022 16:38:40 +0200 +Subject: [PATCH 07/13] gdm/util: Start fingerprint verification once it's + available + +If fingerprint service is not replying fast enough to our async request, +authentication is started but the fingerprint service is never started. + +So, in case the fingerprint type information is received after that the +authentication has been started, let's start the service. + +Part-of: +--- + js/gdm/util.js | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index 6731200f66..24593f6b5c 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -366,6 +366,13 @@ export class ShellUserVerifier extends Signals.EventEmitter { + ? FingerprintReaderType.SWIPE + : FingerprintReaderType.PRESS; + this._updateDefaultService(); ++ ++ if (this._userVerifier && ++ !this._activeServices.has(FINGERPRINT_SERVICE_NAME)) { ++ if (!this._hold?.isAcquired()) ++ this._hold = new Batch.Hold(); ++ await this._maybeStartFingerprintVerification(); ++ } + } + + _onCredentialManagerAuthenticated(credentialManager, _token) { +@@ -617,11 +624,14 @@ export class ShellUserVerifier extends Signals.EventEmitter { + + _beginVerification() { + this._startService(this._getForegroundService()); ++ this._maybeStartFingerprintVerification().catch(logError); ++ } + ++ 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) { +-- +2.51.0 + + +From efb52899f505f1c54120298ac1cc7d3e476a4ff6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Wed, 28 Feb 2024 21:52:55 +0100 +Subject: [PATCH 08/13] js/gdm/util: Rename fingerprint device into devicePath + +It's just the dbus object path, so let's not be confused by that. + +Part-of: +--- + js/gdm/util.js | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index 24593f6b5c..2db9188070 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -355,11 +355,11 @@ export class ShellUserVerifier extends Signals.EventEmitter { + return; + } + +- const [device] = await this._fprintManager.GetDefaultDeviceAsync( ++ const [devicePath] = await this._fprintManager.GetDefaultDeviceAsync( + Gio.DBusCallFlags.NONE, this._cancellable); + const fprintDeviceProxy = new FprintDeviceProxy(Gio.DBus.system, + 'net.reactivated.Fprint', +- device); ++ devicePath); + const fprintDeviceType = fprintDeviceProxy['scan-type']; + + this._fingerprintReaderType = fprintDeviceType === 'swipe' +-- +2.51.0 + + +From 885f1391ab85644377e6080be5ada5960ad209a5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Tue, 12 Apr 2022 16:51:21 +0200 +Subject: [PATCH 09/13] 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 in a not usable state. + +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 + +Part-of: +--- + js/gdm/util.js | 100 +++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 80 insertions(+), 20 deletions(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index 79afbec..2a7644f 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -146,6 +146,7 @@ var ShellUserVerifier = class { + + this._defaultService = null; + this._preemptingService = null; ++ this._fingerprintReaderType = FingerprintReaderType.NONE; + + this._messageQueue = []; + this._messageQueueTimeoutId = 0; +@@ -197,7 +198,8 @@ var ShellUserVerifier = class { + this._userName = userName; + this.reauthenticating = false; + +- this._checkForFingerprintReader().catch(logError); ++ this._checkForFingerprintReader().catch(e => ++ this._handleFingerprintError(e)); + + // If possible, reauthenticate an already running session, + // so any session specific credentials get updated appropriately +@@ -366,39 +368,85 @@ var ShellUserVerifier = class { + this.emit('show-message', null, null, MessageType.NONE); + } + +- _initFingerprintManager() { ++ async _initFingerprintManager() { + if (this._fprintManager) + return; + +- this._fprintManager = new Gio.DBusProxy({ ++ const fprintManager = new Gio.DBusProxy({ + g_connection: Gio.DBus.system, + g_name: 'net.reactivated.Fprint', + g_object_path: '/net/reactivated/Fprint/Manager', + g_interface_name: FprintManagerInfo.name, + g_interface_info: FprintManagerInfo, +- g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, ++ g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES | ++ Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION | ++ Gio.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS, + }); +- this._fprintManager.init(null); ++ ++ try { ++ if (!this._getDetectedDefaultService()) { ++ // Other authentication methods would have already been detected by ++ // now as possibilities if they were available. ++ // If we're here it means that FINGERPRINT_AUTHENTICATION_KEY is ++ // true and so fingerprint authentication is our last potential ++ // option, so go ahead a synchronously look for a fingerprint device ++ // during startup or default service update. ++ fprintManager.init(null); ++ 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']); ++ } else { ++ // Ensure fingerprint service starts, but do not wait for it ++ const cancellable = this._cancellable; ++ await fprintManager.init_async(GLib.PRIORITY_DEFAULT, cancellable); ++ await this._updateFingerprintReaderType(fprintManager, cancellable); ++ this._fprintManager = fprintManager; ++ } ++ } catch (e) { ++ this._handleFingerprintError(e); ++ } + } + +- async _checkForFingerprintReader() { ++ _handleFingerprintError(e) { + this._fingerprintReaderType = FingerprintReaderType.NONE; + ++ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) ++ return; ++ if (e.matches(Gio.DBusError, Gio.DBusError.SERVICE_UNKNOWN)) ++ return; ++ ++ if (Gio.DBusError.is_remote_error(e) && ++ Gio.DBusError.get_remote_error(e) === ++ 'net.reactivated.Fprint.Error.NoSuchDevice') ++ return; ++ ++ logError(e, 'Failed to interact with fprintd service'); ++ } ++ ++ async _checkForFingerprintReader() { + if (!this._fprintManager) { + this._updateDefaultService(); + return; + } + +- const [devicePath] = await this._fprintManager.GetDefaultDeviceAsync( +- Gio.DBusCallFlags.NONE, this._cancellable); ++ if (this._fingerprintReaderType !== FingerprintReaderType.NONE) ++ return; ++ ++ await this._updateFingerprintReaderType(this._fprintManager, this._cancellable); ++ } ++ ++ async _updateFingerprintReaderType(fprintManager, cancellable) { ++ // Wrappers don't support null cancellable, so let's ignore it in case ++ const args = cancellable ? [cancellable] : []; ++ const [devicePath] = await fprintManager.GetDefaultDeviceAsync(...args); + const fprintDeviceProxy = new FprintDeviceProxy(Gio.DBus.system, + 'net.reactivated.Fprint', + devicePath); +- const fprintDeviceType = fprintDeviceProxy['scan-type']; +- +- this._fingerprintReaderType = fprintDeviceType === 'swipe' +- ? FingerprintReaderType.SWIPE +- : FingerprintReaderType.PRESS; ++ this._setFingerprintReaderType(fprintDeviceProxy['scan-type']); + this._updateDefaultService(); + + if (this._userVerifier && +@@ -409,6 +457,14 @@ var ShellUserVerifier = class { + } + } + ++ _setFingerprintReaderType(fprintDeviceType) { ++ this._fingerprintReaderType = ++ FingerprintReaderType[fprintDeviceType.toUpperCase()]; ++ ++ if (this._fingerprintReaderType === undefined) ++ throw new Error(`Unexpected fingerprint device type '${fprintDeviceType}'`); ++ } ++ + _onCredentialManagerAuthenticated(credentialManager, _token) { + this._preemptingService = credentialManager.service; + this.emit('credential-manager-authenticated'); +@@ -584,7 +640,7 @@ var ShellUserVerifier = class { + let needsReset = false; + + if (this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) { +- this._initFingerprintManager(); ++ this._initFingerprintManager().catch(logError); + } else if (this._fingerprintManager) { + this._fingerprintManager = null; + this._fingerprintReaderType = FingerprintReaderType.NONE; +@@ -608,17 +664,21 @@ var ShellUserVerifier = class { + this._cancelAndReset(); + } + +- _updateDefaultService() { +- const oldDefaultService = this._defaultService; +- ++ _getDetectedDefaultService() { + if (this._smartcardManager?.loggedInWithToken()) +- this._defaultService = SMARTCARD_SERVICE_NAME; ++ return SMARTCARD_SERVICE_NAME; + else if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY)) +- this._defaultService = PASSWORD_SERVICE_NAME; ++ return PASSWORD_SERVICE_NAME; + else if (this._smartcardManager) +- this._defaultService = SMARTCARD_SERVICE_NAME; ++ return SMARTCARD_SERVICE_NAME; + else if (this._fingerprintReaderType !== FingerprintReaderType.NONE) +- this._defaultService = FINGERPRINT_SERVICE_NAME; ++ return FINGERPRINT_SERVICE_NAME; ++ return null; ++ } ++ ++ _updateDefaultService() { ++ const oldDefaultService = this._defaultService; ++ this._defaultService = this._getDetectedDefaultService(); + + if (!this._defaultService) { + log("no authentication service is enabled, using password authentication"); +-- +2.51.0 + + +From ce03df5761df3c8571c18c0f291450ccb5ca37cc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Tue, 12 Apr 2022 17:20:58 +0200 +Subject: [PATCH 10/13] gdm/utils: Pass cancellable to the FPrint Device proxy + and avoid signals + +Part-of: +--- + js/gdm/util.js | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index e9ed77e0c9..7adeaebfa1 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -410,8 +410,8 @@ export class ShellUserVerifier extends Signals.EventEmitter { + const args = cancellable ? [cancellable] : []; + const [devicePath] = await fprintManager.GetDefaultDeviceAsync(...args); + const fprintDeviceProxy = new FprintDeviceProxy(Gio.DBus.system, +- 'net.reactivated.Fprint', +- devicePath); ++ 'net.reactivated.Fprint', devicePath, null, cancellable, ++ Gio.DBusProxyFlags.NOT_CONNECT_SIGNALS); + this._setFingerprintReaderType(fprintDeviceProxy['scan-type']); + this._updateDefaultService(); + +-- +2.51.0 + + +From fa25156a6ce0765dfce5a4d5cfe25b3eaa2257f6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Tue, 12 Apr 2022 17:28:57 +0200 +Subject: [PATCH 11/13] gdm/util: Use fully async call to setup the fingerprint + device proxy + +Since fingerprint service can now be started also if a conversation has +already began, we can also initialize the proxy asynchronously, without +the risk that the service won't be started early enough. + +As per this, remove the usage of FprintDeviceProxy wrapper completely +since it's just not giving us anything here. + +Part-of: +--- + js/gdm/util.js | 25 +++++++++++++++++-------- + 1 file changed, 17 insertions(+), 8 deletions(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index eb81b34..a980918 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -15,8 +15,8 @@ const SmartcardManager = imports.misc.smartcardManager; + + const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml( + loadInterfaceXML('net.reactivated.Fprint.Manager')); +-const FprintDeviceIface = loadInterfaceXML('net.reactivated.Fprint.Device'); +-const FprintDeviceProxy = Gio.DBusProxy.makeProxyWrapper(FprintDeviceIface); ++const FprintDeviceInfo = Gio.DBusInterfaceInfo.new_for_xml( ++ loadInterfaceXML('net.reactivated.Fprint.Device')); + + Gio._promisify(Gdm.Client.prototype, + 'open_reauthentication_channel', 'open_reauthentication_channel_finish'); +@@ -395,9 +395,8 @@ var ShellUserVerifier = class { + 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); ++ const fprintDeviceProxy = this._getFingerprintDeviceProxy(devicePath); ++ fprintDeviceProxy.init(null); + this._setFingerprintReaderType(fprintDeviceProxy['scan-type']); + } else { + // Ensure fingerprint service starts, but do not wait for it +@@ -411,6 +410,17 @@ var ShellUserVerifier = class { + } + } + ++ _getFingerprintDeviceProxy(devicePath) { ++ return new Gio.DBusProxy({ ++ g_connection: Gio.DBus.system, ++ g_name: 'net.reactivated.Fprint', ++ g_object_path: devicePath, ++ g_interface_name: FprintDeviceInfo.name, ++ g_interface_info: FprintDeviceInfo, ++ g_flags: Gio.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS, ++ }); ++ } ++ + _handleFingerprintError(e) { + this._fingerprintReaderType = FingerprintReaderType.NONE; + +@@ -443,9 +453,8 @@ var ShellUserVerifier = class { + // Wrappers don't support null cancellable, so let's ignore it in case + const args = cancellable ? [cancellable] : []; + const [devicePath] = await fprintManager.GetDefaultDeviceAsync(...args); +- const fprintDeviceProxy = new FprintDeviceProxy(Gio.DBus.system, +- 'net.reactivated.Fprint', devicePath, null, cancellable, +- Gio.DBusProxyFlags.NOT_CONNECT_SIGNALS); ++ const fprintDeviceProxy = this._getFingerprintDeviceProxy(devicePath); ++ await fprintDeviceProxy.init_async(GLib.PRIORITY_DEFAULT, cancellable); + this._setFingerprintReaderType(fprintDeviceProxy['scan-type']); + this._updateDefaultService(); + +-- +2.51.0 + + +From 7552875dbc78eb52e7d3b5a9f784c175ecca585c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Tue, 12 Apr 2022 17:05:02 +0200 +Subject: [PATCH 12/13] gdm/util: Reduce the fprintd proxy wait timeout + +Given that this may lead to the shell to hang on gdm startup, and that +we expect the service to be up and running quickly, we can safely set a +5 seconds timeout instead of using the longer GLib proxy defaults. + +Part-of: +--- + js/gdm/util.js | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index a980918..924bd2d 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -46,6 +46,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 = { +@@ -392,6 +393,10 @@ var ShellUserVerifier = class { + // option, so go ahead a synchronously look for a fingerprint device + // during startup or default service update. + fprintManager.init(null); ++ // Do not wait too much for fprintd to reply, as in case it hangs ++ // we should fail early without having the shell to misbehave ++ fprintManager.set_default_timeout(FINGERPRINT_SERVICE_PROXY_TIMEOUT); ++ + const [devicePath] = fprintManager.GetDefaultDeviceSync(); + this._fprintManager = fprintManager; + +-- +2.51.0 + + +From 9075fc4733c62de18ab6c83e0aa32d26dd055569 Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Mon, 9 Mar 2026 15:26:50 +0100 +Subject: [PATCH 13/13] gdm/util: Fix fprintManager DBus calls for older GJS + +The auto-generated GetDefaultDeviceSync/Async method wrappers don't +exist in this version. Use explicit call_sync/call methods instead. +--- + js/gdm/util.js | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index 105d5d4..38a7015 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -26,6 +26,8 @@ Gio._promisify(Gdm.UserVerifierProxy.prototype, + 'call_begin_verification_for_user', 'call_begin_verification_for_user_finish'); + Gio._promisify(Gdm.UserVerifierProxy.prototype, + 'call_begin_verification', 'call_begin_verification_finish'); ++Gio._promisify(Gio.DBusProxy.prototype, ++ 'call', 'call_finish'); + + var PASSWORD_SERVICE_NAME = 'gdm-password'; + var FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; +@@ -397,7 +399,13 @@ var ShellUserVerifier = class { + // we should fail early without having the shell to misbehave + fprintManager.set_default_timeout(FINGERPRINT_SERVICE_PROXY_TIMEOUT); + +- const [devicePath] = fprintManager.GetDefaultDeviceSync(); ++ const result = fprintManager.call_sync( ++ 'GetDefaultDevice', ++ null, ++ Gio.DBusCallFlags.NONE, ++ FINGERPRINT_SERVICE_PROXY_TIMEOUT, ++ null); ++ const [devicePath] = result.deep_unpack(); + this._fprintManager = fprintManager; + + const fprintDeviceProxy = this._getFingerprintDeviceProxy(devicePath); +@@ -455,9 +463,13 @@ var ShellUserVerifier = class { + } + + async _updateFingerprintReaderType(fprintManager, cancellable) { +- // Wrappers don't support null cancellable, so let's ignore it in case +- const args = cancellable ? [cancellable] : []; +- const [devicePath] = await fprintManager.GetDefaultDeviceAsync(...args); ++ const result = await fprintManager.call( ++ 'GetDefaultDevice', ++ null, ++ Gio.DBusCallFlags.NONE, ++ -1, ++ cancellable); ++ const [devicePath] = result.deep_unpack(); + const fprintDeviceProxy = this._getFingerprintDeviceProxy(devicePath); + await fprintDeviceProxy.init_async(GLib.PRIORITY_DEFAULT, cancellable); + this._setFingerprintReaderType(fprintDeviceProxy['scan-type']); diff --git a/gnome-shell.spec b/gnome-shell.spec index 0ec1f82..7aea317 100644 --- a/gnome-shell.spec +++ b/gnome-shell.spec @@ -8,7 +8,7 @@ Name: gnome-shell Version: 40.10 -Release: 33%{?dist} +Release: 34%{?dist} Summary: Window management and application launching for GNOME License: GPLv2+ @@ -37,7 +37,8 @@ Patch18: 0001-authPrompt-Disregard-smartcard-status-changes-events.patch Patch19: 0001-loginDialog-Show-session-menu-button-when-in-IN_PROG.patch Patch20: 0001-systemActions-Optionally-allow-restart-shutdown-on-l.patch Patch21: 0001-authPrompt-Connect-disable-show-password-key-with-pa.patch -Patch22: 0001-main-Register-session-with-GDM-on-startup.patch +Patch22: 0001-gdm-util-Early-initialize-all-internal-properties.patch +Patch23: 0001-main-Register-session-with-GDM-on-startup.patch # Misc. Patch30: 0001-panel-add-an-icon-to-the-ActivitiesButton.patch @@ -307,6 +308,10 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/evolution-calendar.de %endif %changelog +* Mon Mar 9 2026 Joan Torres Lopez - 40.10-34 +- Fix to automatically start fingerprint when enabled + Resolves: RHEL-4166 + * Tue Nov 18 2025 Joan Torres Lopez - 40.10-33 - Register session with GDM on startup Resolves: RHEL-129287