Open
[sync] PR-51: Fix CVE-2024-36472: Show notification when detecting captive portal #55
openeuler-ci-botAdd on Jun 2
[sync] PR-51: Fix CVE-2024-36472: Show notification when detecting captive portal #55
Open
Total: 2 files changed+260-1
| @@ -0,0 +1,255 @@ | |||
| 1 | +From 57de9ee874bff07b71dc323e54d5d721c4ded7fe Mon Sep 17 00:00:00 2001 | ||
| 2 | +From: Florian Müllner <fmuellner@gnome.org> | ||
| 3 | +Date: Wed, 12 Jun 2024 13:13:41 +0200 | ||
| 4 | +Subject: [PATCH] network: Show notification when detecting captive portal | ||
| 5 | + | ||
| 6 | +When NetworkManager detects limited connectivity, we currently | ||
| 7 | +pop up the portal helper window immediately. This can both be | ||
| 8 | +disruptive when it happens unexpectedly, and unnoticeable | ||
| 9 | +when it happens during screen lock. | ||
| 10 | + | ||
| 11 | +In any case, it seems better to not pop up a window without | ||
| 12 | +explicit user action, so instead show a notification that | ||
| 13 | +launches the portal window when activated. | ||
| 14 | + | ||
| 15 | +Backport of upstream commits: | ||
| 16 | + 57de9ee874b - network: Split out CaptivePortalHandler class | ||
| 17 | + 4ab1ccf3f21 - status/network: Show notification when detecting captive portal | ||
| 18 | + | ||
| 19 | +Adapted for gnome-shell 44.6 (openEuler-24.03-LTS-SP2). | ||
| 20 | + | ||
| 21 | +CVE: CVE-2024-36472 | ||
| 22 | +Upstream: https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3408 | ||
| 23 | +--- | ||
| 24 | + js/ui/status/network.js | 185 ++++++++++++++++++++++++++-------------- | ||
| 25 | + 1 file changed, 121 insertions(+), 64 deletions(-) | ||
| 26 | + | ||
| 27 | +diff --git a/js/ui/status/network.js b/js/ui/status/network.js | ||
| 28 | +--- a/js/ui/status/network.js 2026-06-01 17:54:49.560042031 +0800 | ||
| 29 | ++++ b/js/ui/status/network.js 2026-06-01 17:55:31.887793617 +0800 | ||
| 30 | +@@ -5,6 +5,7 @@ | ||
| 31 | + const Main = imports.ui.main; | ||
| 32 | + const PopupMenu = imports.ui.popupMenu; | ||
| 33 | + const MessageTray = imports.ui.messageTray; | ||
| 34 | ++const Signals = imports.misc.signals; | ||
| 35 | + const ModemManager = imports.misc.modemManager; | ||
| 36 | + const Util = imports.misc.util; | ||
| 37 | + | ||
| 38 | +@@ -1932,11 +1933,115 @@ | ||
| 39 | + }); | ||
| 40 | + | ||
| 41 | + var Indicator = GObject.registerClass( | ||
| 42 | ++ } | ||
| 43 | ++}); | ||
| 44 | ++ | ||
| 45 | ++class CaptivePortalHandler extends Signals.EventEmitter { | ||
| 46 | ++ constructor(checkUri) { | ||
| 47 | ++ super(); | ||
| 48 | ++ | ||
| 49 | ++ this._checkUri = checkUri; | ||
| 50 | ++ this._connectivityQueue = new Set(); | ||
| 51 | ++ this._notifications = new Map(); | ||
| 52 | ++ this._portalHelperProxy = null; | ||
| 53 | ++ } | ||
| 54 | ++ | ||
| 55 | ++ addConnection(name, path) { | ||
| 56 | ++ if (this._connectivityQueue.has(path) || this._notifications.has(path)) | ||
| 57 | ++ return; | ||
| 58 | ++ | ||
| 59 | ++ const source = new MessageTray.SystemNotificationSource(); | ||
| 60 | ++ source.connect('destroy', () => {}); | ||
| 61 | ++ Main.messageTray.add(source); | ||
| 62 | ++ | ||
| 63 | ++ const notification = new MessageTray.Notification(source, | ||
| 64 | ++ _('Sign Into Wi–Fi Network'), | ||
| 65 | ++ name); | ||
| 66 | ++ notification.connect('activated', | ||
| 67 | ++ () => this._onNotificationActivated(path)); | ||
| 68 | ++ notification.connect('destroy', | ||
| 69 | ++ () => this._notifications.delete(path)); | ||
| 70 | ++ this._notifications.set(path, notification); | ||
| 71 | ++ source.showNotification(notification); | ||
| 72 | ++ } | ||
| 73 | ++ | ||
| 74 | ++ | ||
| 75 | ++ removeConnection(path) { | ||
| 76 | ++ if (this._connectivityQueue.delete(path)) | ||
| 77 | ++ this._portalHelperProxy?.CloseAsync(path); | ||
| 78 | ++ this._notifications.get(path)?.destroy( | ||
| 79 | ++ MessageTray.NotificationDestroyedReason.SOURCE_CLOSED); | ||
| 80 | ++ this._notifications.delete(path); | ||
| 81 | ++ } | ||
| 82 | ++ | ||
| 83 | ++ _onNotificationActivated(path) { | ||
| 84 | ++ this._launchPortalHelper(path).catch(logError); | ||
| 85 | ++ | ||
| 86 | ++ Main.overview.hide(); | ||
| 87 | ++ Main.panel.closeCalendar(); | ||
| 88 | ++ } | ||
| 89 | ++ | ||
| 90 | ++ _portalHelperDone(parameters) { | ||
| 91 | ++ const [path, result] = parameters; | ||
| 92 | ++ | ||
| 93 | ++ if (result === PortalHelperResult.CANCELLED) { | ||
| 94 | ++ // Keep the connection in the queue, so the user is not | ||
| 95 | ++ // spammed with more logins until we next flush the queue, | ||
| 96 | ++ // which will happen once they choose a better connection | ||
| 97 | ++ // or we get to full connectivity through other means | ||
| 98 | ++ } else if (result === PortalHelperResult.COMPLETED) { | ||
| 99 | ++ this.removeConnection(path); | ||
| 100 | ++ } else if (result === PortalHelperResult.RECHECK) { | ||
| 101 | ++ this.emit('recheck', path); | ||
| 102 | ++ } else { | ||
| 103 | ++ log(`Invalid result from portal helper: ${result}`); | ||
| 104 | ++ } | ||
| 105 | ++ } | ||
| 106 | ++ | ||
| 107 | ++ async _launchPortalHelper(path) { | ||
| 108 | ++ const timestamp = global.get_current_time(); | ||
| 109 | ++ if (!this._portalHelperProxy) { | ||
| 110 | ++ this._portalHelperProxy = new Gio.DBusProxy({ | ||
| 111 | ++ g_connection: Gio.DBus.session, | ||
| 112 | ++ g_name: 'org.gnome.Shell.PortalHelper', | ||
| 113 | ++ g_object_path: '/org/gnome/Shell/PortalHelper', | ||
| 114 | ++ g_interface_name: PortalHelperInfo.name, | ||
| 115 | ++ g_interface_info: PortalHelperInfo, | ||
| 116 | ++ }); | ||
| 117 | ++ this._portalHelperProxy.connectSignal('Done', | ||
| 118 | ++ (proxy, emitter, params) => { | ||
| 119 | ++ this._portalHelperDone(params); | ||
| 120 | ++ }); | ||
| 121 | ++ | ||
| 122 | ++ try { | ||
| 123 | ++ await this._portalHelperProxy.init_async( | ||
| 124 | ++ GLib.PRIORITY_DEFAULT, null); | ||
| 125 | ++ } catch (e) { | ||
| 126 | ++ console.error(`Error launching the portal helper: ${e.message}`); | ||
| 127 | ++ } | ||
| 128 | ++ } | ||
| 129 | ++ | ||
| 130 | ++ this._portalHelperProxy?.AuthenticateAsync(path, this._checkUri, timestamp).catch(logError); | ||
| 131 | ++ this._connectivityQueue.add(path); | ||
| 132 | ++ } | ||
| 133 | ++ | ||
| 134 | ++ clear() { | ||
| 135 | ++ for (const item of this._connectivityQueue) | ||
| 136 | ++ this._portalHelperProxy?.CloseAsync(item); | ||
| 137 | ++ this._connectivityQueue.clear(); | ||
| 138 | ++ | ||
| 139 | ++ for (const n of this._notifications.values()) | ||
| 140 | ++ n.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED); | ||
| 141 | ++ this._notifications.clear(); | ||
| 142 | ++ } | ||
| 143 | ++} | ||
| 144 | ++ | ||
| 145 | + class Indicator extends SystemIndicator { | ||
| 146 | + _init() { | ||
| 147 | + super._init(); | ||
| 148 | + | ||
| 149 | +- this._connectivityQueue = new Set(); | ||
| 150 | ++ this._portalHandler = null; | ||
| 151 | ++ | ||
| 152 | + | ||
| 153 | + this._mainConnection = null; | ||
| 154 | + | ||
| 155 | +@@ -1991,6 +2096,16 @@ | ||
| 156 | + this, 'visible', | ||
| 157 | + GObject.BindingFlags.SYNC_CREATE); | ||
| 158 | + | ||
| 159 | ++ const {connectivityCheckUri} = this._client; | ||
| 160 | ++ this._portalHandler = new CaptivePortalHandler(connectivityCheckUri); | ||
| 161 | ++ this._portalHandler.connect('recheck', async (o, path) => { | ||
| 162 | ++ try { | ||
| 163 | ++ const state = await this._client.check_connectivity_async(null); | ||
| 164 | ++ if (state >= NM.ConnectivityState.FULL) | ||
| 165 | ++ this._portalHandler.removeConnection(path); | ||
| 166 | ++ } catch (e) { } | ||
| 167 | ++ }); | ||
| 168 | ++ | ||
| 169 | + this._client.connectObject( | ||
| 170 | + 'notify::primary-connection', () => this._syncMainConnection(), | ||
| 171 | + 'notify::activating-connection', () => this._syncMainConnection(), | ||
| 172 | +@@ -2055,42 +2170,10 @@ | ||
| 173 | + this._notification?.destroy(); | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | +- _flushConnectivityQueue() { | ||
| 177 | +- for (let item of this._connectivityQueue) | ||
| 178 | +- this._portalHelperProxy?.CloseAsync(item); | ||
| 179 | +- this._connectivityQueue.clear(); | ||
| 180 | +- } | ||
| 181 | +- | ||
| 182 | +- _closeConnectivityCheck(path) { | ||
| 183 | +- if (this._connectivityQueue.delete(path)) | ||
| 184 | +- this._portalHelperProxy?.CloseAsync(path); | ||
| 185 | +- } | ||
| 186 | +- | ||
| 187 | +- async _portalHelperDone(parameters) { | ||
| 188 | +- let [path, result] = parameters; | ||
| 189 | +- | ||
| 190 | +- if (result == PortalHelperResult.CANCELLED) { | ||
| 191 | +- // Keep the connection in the queue, so the user is not | ||
| 192 | +- // spammed with more logins until we next flush the queue, | ||
| 193 | +- // which will happen once they choose a better connection | ||
| 194 | +- // or we get to full connectivity through other means | ||
| 195 | +- } else if (result == PortalHelperResult.COMPLETED) { | ||
| 196 | +- this._closeConnectivityCheck(path); | ||
| 197 | +- } else if (result == PortalHelperResult.RECHECK) { | ||
| 198 | +- try { | ||
| 199 | +- const state = await this._client.check_connectivity_async(null); | ||
| 200 | +- if (state >= NM.ConnectivityState.FULL) | ||
| 201 | +- this._closeConnectivityCheck(path); | ||
| 202 | +- } catch (e) { } | ||
| 203 | +- } else { | ||
| 204 | +- log(`Invalid result from portal helper: ${result}`); | ||
| 205 | +- } | ||
| 206 | +- } | ||
| 207 | +- | ||
| 208 | +- async _syncConnectivity() { | ||
| 209 | ++ _syncConnectivity() { | ||
| 210 | + if (this._mainConnection == null || | ||
| 211 | + this._mainConnection.state != NM.ActiveConnectionState.ACTIVATED) { | ||
| 212 | +- this._flushConnectivityQueue(); | ||
| 213 | ++ this._portalHandler.clear(); | ||
| 214 | + return; | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | +@@ -2105,35 +2188,9 @@ | ||
| 218 | + if (!isPortal || Main.sessionMode.isGreeter) | ||
| 219 | + return; | ||
| 220 | + | ||
| 221 | +- let path = this._mainConnection.get_path(); | ||
| 222 | +- if (this._connectivityQueue.has(path)) | ||
| 223 | +- return; | ||
| 224 | +- | ||
| 225 | +- let timestamp = global.get_current_time(); | ||
| 226 | +- if (!this._portalHelperProxy) { | ||
| 227 | +- this._portalHelperProxy = new Gio.DBusProxy({ | ||
| 228 | +- g_connection: Gio.DBus.session, | ||
| 229 | +- g_name: 'org.gnome.Shell.PortalHelper', | ||
| 230 | +- g_object_path: '/org/gnome/Shell/PortalHelper', | ||
| 231 | +- g_interface_name: PortalHelperInfo.name, | ||
| 232 | +- g_interface_info: PortalHelperInfo, | ||
| 233 | +- }); | ||
| 234 | +- this._portalHelperProxy.connectSignal('Done', | ||
| 235 | +- (proxy, emitter, params) => { | ||
| 236 | +- this._portalHelperDone(params).catch(logError); | ||
| 237 | +- }); | ||
| 238 | +- | ||
| 239 | +- try { | ||
| 240 | +- await this._portalHelperProxy.init_async( | ||
| 241 | +- GLib.PRIORITY_DEFAULT, null); | ||
| 242 | +- } catch (e) { | ||
| 243 | +- console.error(`Error launching the portal helper: ${e.message}`); | ||
| 244 | +- } | ||
| 245 | +- } | ||
| 246 | +- | ||
| 247 | +- this._portalHelperProxy?.AuthenticateAsync(path, this._client.connectivity_check_uri, timestamp).catch(logError); | ||
| 248 | +- | ||
| 249 | +- this._connectivityQueue.add(path); | ||
| 250 | ++ this._portalHandler.addConnection( | ||
| 251 | ++ this._mainConnection.get_id(), | ||
| 252 | ++ this._mainConnection.get_path()); | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + _updateIcon() { | ||
| @@ -17,7 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | Name: gnome-shell | 18 | Name: gnome-shell |
| 19 | Version: 44.6 | 19 | Version: 44.6 |
| 20 | -Release: 1 | 20 | +Release: 2 |
| 21 | Summary: Core user interface functions for the GNOME 3 desktop | 21 | Summary: Core user interface functions for the GNOME 3 desktop |
| 22 | Group: User Interface/Desktops | 22 | Group: User Interface/Desktops |
| 23 | License: GPLv2+ | 23 | License: GPLv2+ |
| @@ -25,6 +25,7 @@ URL: https://wiki.gnome.org/Projects/GnomeShell | |||
| 25 | Source0: https://download.gnome.org/sources/gnome-shell/44/%{name}-%{version}.tar.xz | 25 | Source0: https://download.gnome.org/sources/gnome-shell/44/%{name}-%{version}.tar.xz |
| 26 | Patch10001: gnome-shell-favourite-apps-firefox.patch | 26 | Patch10001: gnome-shell-favourite-apps-firefox.patch |
| 27 | Patch40001: 0001-gdm-Work-around-failing-fingerprint-auth.patch | 27 | Patch40001: 0001-gdm-Work-around-failing-fingerprint-auth.patch |
| 28 | +Patch40002: backport-CVE-2024-36472.patch | ||
| 28 | 29 | ||
| 29 | BuildRequires: bash-completion gcc meson mesa-libGL-devel mesa-libEGL-devel | 30 | BuildRequires: bash-completion gcc meson mesa-libGL-devel mesa-libEGL-devel |
| 30 | BuildRequires: gtk-doc desktop-file-utils python3 librsvg2-devel chrpath cmake | 31 | BuildRequires: gtk-doc desktop-file-utils python3 librsvg2-devel chrpath cmake |
| @@ -186,6 +187,9 @@ glib-compile-schemas --allow-any-name %{_datadir}/glib-2.0/schemas &> /dev/null | |||
| 186 | %{_mandir}/man1/gnome-shell.1* | 187 | %{_mandir}/man1/gnome-shell.1* |
| 187 | 188 | ||
| 188 | %changelog | 189 | %changelog |
| 190 | +* Mon Jun 01 2026 herengui <herengui@kylinsec.com.cn> - 44.6-2 | ||
| 191 | +- Fix CVE-2024-36472: Show notification with user activation before launching portal helper | ||
| 192 | + | ||
| 189 | * Mon Nov 27 2023 lwg <liweiganga@uniontech.com> - 44.6-1 | 193 | * Mon Nov 27 2023 lwg <liweiganga@uniontech.com> - 44.6-1 |
| 190 | - update to version 44.6 | 194 | - update to version 44.6 |
| 191 | 195 | ||