From 2a3e297218c85dd1d198bdecdd0768ae85e97f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 16 Jun 2021 19:09:42 +0200 Subject: [PATCH] introspect: Split out DBusSenderChecker Restricting callers to a list of allowed senders is useful for other D-Bus services as well, so split out the existing code into a reusable class. https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3943 Part-of: --- js/misc/introspect.js | 30 +++-------------------- js/misc/util.js | 57 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/js/misc/introspect.js b/js/misc/introspect.js index 8e469fbea..22bd8319c 100644 --- a/js/misc/introspect.js +++ b/js/misc/introspect.js @@ -9,6 +9,7 @@ const APP_ALLOWLIST = [ const INTROSPECT_DBUS_API_VERSION = 3; const { loadInterfaceXML } = imports.misc.fileUtils; +const { DBusSenderChecker } = imports.misc.util; const IntrospectDBusIface = loadInterfaceXML('org.gnome.Shell.Introspect'); @@ -43,14 +44,7 @@ var IntrospectService = class { this._syncRunningApplications(); - this._allowlistMap = new Map(); - APP_ALLOWLIST.forEach(appName => { - Gio.DBus.watch_name(Gio.BusType.SESSION, - appName, - Gio.BusNameWatcherFlags.NONE, - (conn, name, owner) => this._allowlistMap.set(name, owner), - (conn, name) => this._allowlistMap.delete(name)); - }); + this._senderChecker = new DBusSenderChecker(APP_ALLOWLIST); this._settings = St.Settings.get(); this._settings.connect('notify::enable-animations', @@ -67,10 +61,6 @@ var IntrospectService = class { return app.get_windows().some(w => w.transient_for == null); } - _isSenderAllowed(sender) { - return [...this._allowlistMap.values()].includes(sender); - } - _getSandboxedAppId(app) { let ids = app.get_windows().map(w => w.get_sandboxed_app_id()); return ids.find(id => id != null); @@ -127,21 +117,9 @@ var IntrospectService = class { type == Meta.WindowType.UTILITY; } - _checkInvocation(invocation) { - if (global.context.unsafe_mode) - return; - - if (this._isSenderAllowed(invocation.get_sender())) - return; - - throw new GLib.Error(Gio.DBusError, - Gio.DBusError.ACCESS_DENIED, - 'App introspection not allowed'); - } - GetRunningApplicationsAsync(params, invocation) { try { - this._checkInvocation(invocation); + this._senderChecker.checkInvocation(invocation); } catch (e) { invocation.return_gerror(e); return; @@ -156,7 +134,7 @@ var IntrospectService = class { let windowsList = {}; try { - this._checkInvocation(invocation); + this._senderChecker.checkInvocation(invocation); } catch (e) { invocation.return_gerror(e); return; diff --git a/js/misc/util.js b/js/misc/util.js index 8139d3f47..bd5718472 100644 --- a/js/misc/util.js +++ b/js/misc/util.js @@ -1,7 +1,8 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /* exported findUrls, spawn, spawnCommandLine, spawnApp, trySpawnCommandLine, formatTime, formatTimeSpan, createTimeLabel, insertSorted, - ensureActorVisibleInScrollView, wiggle, lerp, GNOMEversionCompare */ + ensureActorVisibleInScrollView, wiggle, lerp, GNOMEversionCompare, + DBusSenderChecker */ const { Clutter, Gio, GLib, Shell, St, GnomeDesktop } = imports.gi; const Gettext = imports.gettext; @@ -477,3 +478,57 @@ function GNOMEversionCompare(version1, version2) { return 0; } + +var DBusSenderChecker = class { + /** + * @param {string[]} allowList - list of allowed well-known names + */ + constructor(allowList) { + this._allowlistMap = new Map(); + + this._watchList = allowList.map(name => { + return Gio.DBus.watch_name(Gio.BusType.SESSION, + name, + Gio.BusNameWatcherFlags.NONE, + (conn_, name_, owner) => this._allowlistMap.set(name, owner), + () => this._allowlistMap.delete(name)); + }); + } + + /** + * @param {string} sender - the bus name that invoked the checked method + * @returns {bool} + */ + _isSenderAllowed(sender) { + return [...this._allowlistMap.values()].includes(sender); + } + + /** + * Check whether the bus name that invoked @invocation maps + * to an entry in the allow list. + * + * @throws + * @param {Gio.DBusMethodInvocation} invocation - the invocation + * @returns {void} + */ + checkInvocation(invocation) { + if (global.context.unsafe_mode) + return; + + if (this._isSenderAllowed(invocation.get_sender())) + return; + + throw new GLib.Error(Gio.DBusError, + Gio.DBusError.ACCESS_DENIED, + '%s is not allowed'.format(invocation.get_method_name())); + } + + /** + * @returns {void} + */ + destroy() { + for (const id in this._watchList) + Gio.DBus.unwatch_name(id); + this._watchList = []; + } +};