diff --git a/data/org.gnome.shell.gschema.xml.in b/data/org.gnome.shell.gschema.xml.in
index cd6a2356ddf8ad86f1dd89f5f33812e24d9f08a1..a805d5262c698f4e41b6f034c82f28801e6ff655 100644
--- a/data/org.gnome.shell.gschema.xml.in
+++ b/data/org.gnome.shell.gschema.xml.in
@@ -104,14 +104,6 @@
number can be used to effectively disable the dialog.
-
- false
- Enable introspection API
-
- Enables a D-Bus API that allows to introspect the application state of
- the shell.
-
-
{
@@ -49,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',
@@ -73,14 +61,6 @@ var IntrospectService = class {
return app.get_windows().some(w => w.transient_for == null);
}
- _isIntrospectEnabled() {
- return this._introspectSettings.get_boolean(INTROSPECT_KEY);
- }
-
- _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);
@@ -137,21 +117,11 @@ var IntrospectService = class {
type == Meta.WindowType.UTILITY;
}
- _isInvocationAllowed(invocation) {
- if (this._isIntrospectEnabled())
- return true;
-
- if (this._isSenderAllowed(invocation.get_sender()))
- return true;
-
- return false;
- }
-
GetRunningApplicationsAsync(params, invocation) {
- if (!this._isInvocationAllowed(invocation)) {
- invocation.return_error_literal(Gio.DBusError,
- Gio.DBusError.ACCESS_DENIED,
- 'App introspection not allowed');
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
return;
}
@@ -163,10 +133,10 @@ var IntrospectService = class {
let apps = this._appSystem.get_running();
let windowsList = {};
- if (!this._isInvocationAllowed(invocation)) {
- invocation.return_error_literal(Gio.DBusError,
- Gio.DBusError.ACCESS_DENIED,
- 'App introspection not allowed');
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
return;
}
diff --git a/js/misc/util.js b/js/misc/util.js
index 8139d3f47cfcce2200f72731d5768210dd96ff96..bd5718472835672bc48b8761d9b401c94f41bb70 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 = [];
+ }
+};
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 89b082ad05f86c68b928e931a22243ff3486dce7..dafba690a57075d293201cf919d15c02451d0d9e 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -507,6 +507,20 @@ class PanelCorner extends St.DrawingArea {
}
});
+const UnsafeModeIndicator = GObject.registerClass(
+class UnsafeModeIndicator extends PanelMenu.SystemIndicator {
+ _init() {
+ super._init();
+
+ this._indicator = this._addIndicator();
+ this._indicator.icon_name = 'channel-insecure-symbolic';
+
+ global.context.bind_property('unsafe-mode',
+ this._indicator, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+});
+
var AggregateLayout = GObject.registerClass(
class AggregateLayout extends Clutter.BoxLayout {
_init(params = {}) {
@@ -568,6 +582,7 @@ class AggregateMenu extends PanelMenu.Button {
this._location = new imports.ui.status.location.Indicator();
this._nightLight = new imports.ui.status.nightLight.Indicator();
this._thunderbolt = new imports.ui.status.thunderbolt.Indicator();
+ this._unsafeMode = new UnsafeModeIndicator();
this._indicators.add_child(this._remoteAccess);
this._indicators.add_child(this._thunderbolt);
@@ -579,6 +594,7 @@ class AggregateMenu extends PanelMenu.Button {
this._indicators.add_child(this._bluetooth);
this._indicators.add_child(this._rfkill);
this._indicators.add_child(this._volume);
+ this._indicators.add_child(this._unsafeMode);
this._indicators.add_child(this._power);
this._indicators.add_child(this._powerProfiles);
diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js
index 81ab516b171483c8f8300e58bea72b753218c500..bf537b7d6d083326d3f8cce2449a57821c20cc73 100644
--- a/js/ui/screenshot.js
+++ b/js/ui/screenshot.js
@@ -15,6 +15,7 @@ Gio._promisify(Shell.Screenshot.prototype,
'screenshot_area', 'screenshot_area_finish');
const { loadInterfaceXML } = imports.misc.fileUtils;
+const { DBusSenderChecker } = imports.misc.util;
const ScreenshotIface = loadInterfaceXML('org.gnome.Shell.Screenshot');
@@ -24,6 +25,12 @@ var ScreenshotService = class {
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
this._screenShooter = new Map();
+ this._senderChecker = new DBusSenderChecker([
+ 'org.gnome.SettingsDaemon.MediaKeys',
+ 'org.freedesktop.impl.portal.desktop.gtk',
+ 'org.freedesktop.impl.portal.desktop.gnome',
+ 'org.gnome.Screenshot',
+ ]);
this._lockdownSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
@@ -46,6 +53,13 @@ var ScreenshotService = class {
Gio.IOErrorEnum, Gio.IOErrorEnum.PERMISSION_DENIED,
'Saving to disk is disabled');
return null;
+ } else {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return null;
+ }
}
let shooter = new Shell.Screenshot();
@@ -254,6 +268,13 @@ var ScreenshotService = class {
}
async SelectAreaAsync(params, invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
let selectArea = new SelectArea();
try {
let areaRectangle = await selectArea.selectAsync();
@@ -269,6 +290,13 @@ var ScreenshotService = class {
}
FlashAreaAsync(params, invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
let [x, y, width, height] = params;
[x, y, width, height] = this._scaleArea(x, y, width, height);
if (!this._checkArea(x, y, width, height)) {
diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js
index 6574cc5288a897df9efb437be359c5eb139d424c..a8070eb925cc7348b8467e413be7f301278bd911 100644
--- a/js/ui/shellDBus.js
+++ b/js/ui/shellDBus.js
@@ -10,6 +10,7 @@ const Main = imports.ui.main;
const Screenshot = imports.ui.screenshot;
const { loadInterfaceXML } = imports.misc.fileUtils;
+const { DBusSenderChecker } = imports.misc.util;
const { ControlsState } = imports.ui.overviewControls;
const GnomeShellIface = loadInterfaceXML('org.gnome.Shell');
@@ -20,6 +21,11 @@ var GnomeShell = class {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
+ this._senderChecker = new DBusSenderChecker([
+ 'org.gnome.ControlCenter',
+ 'org.gnome.SettingsDaemon.MediaKeys',
+ ]);
+
this._extensionsService = new GnomeShellExtensions();
this._screenshotService = new Screenshot.ScreenshotService();
@@ -54,7 +60,7 @@ var GnomeShell = class {
*
*/
Eval(code) {
- if (!global.settings.get_boolean('development-tools'))
+ if (!global.context.unsafe_mode)
return [false, ''];
let returnValue;
@@ -72,11 +78,40 @@ var GnomeShell = class {
return [success, returnValue];
}
- FocusSearch() {
+ /**
+ * Focus the overview's search entry
+ *
+ * @param {...any} params - method parameters
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ FocusSearchAsync(params, invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
Main.overview.focusSearch();
+ invocation.return_value(null);
}
- ShowOSD(params) {
+ /**
+ * Show OSD with the specified parameters
+ *
+ * @param {...any} params - method parameters
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ ShowOSDAsync([params], invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
for (let param in params)
params[param] = params[param].deep_unpack();
@@ -97,17 +132,55 @@ var GnomeShell = class {
icon = Gio.Icon.new_for_string(serializedIcon);
Main.osdWindowManager.show(monitorIndex, icon, label, level, maxLevel);
+ invocation.return_value(null);
}
- FocusApp(id) {
+ /**
+ * Focus specified app in the overview's app grid
+ *
+ * @param {string} id - an application ID
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ FocusAppAsync([id], invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
Main.overview.selectApp(id);
+ invocation.return_value(null);
}
- ShowApplications() {
+ /**
+ * Show the overview's app grid
+ *
+ * @param {...any} params - method parameters
+ * @param {Gio.DBusMethodInvocation} invocation - the invocation
+ * @returns {void}
+ */
+ ShowApplicationsAsync(params, invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
Main.overview.show(ControlsState.APP_GRID);
+ invocation.return_value(null);
}
GrabAcceleratorAsync(params, invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
let [accel, modeFlags, grabFlags] = params;
let sender = invocation.get_sender();
let bindingAction = this._grabAcceleratorForSender(accel, modeFlags, grabFlags, sender);
@@ -115,6 +188,13 @@ var GnomeShell = class {
}
GrabAcceleratorsAsync(params, invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
let [accels] = params;
let sender = invocation.get_sender();
let bindingActions = [];
@@ -126,6 +206,13 @@ var GnomeShell = class {
}
UngrabAcceleratorAsync(params, invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
let [action] = params;
let sender = invocation.get_sender();
let ungrabSucceeded = this._ungrabAcceleratorForSender(action, sender);
@@ -134,6 +221,13 @@ var GnomeShell = class {
}
UngrabAcceleratorsAsync(params, invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
let [actions] = params;
let sender = invocation.get_sender();
let ungrabSucceeded = true;
@@ -214,6 +308,13 @@ var GnomeShell = class {
}
ShowMonitorLabelsAsync(params, invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
let sender = invocation.get_sender();
let [dict] = params;
Main.osdMonitorLabeler.show(sender, dict);
@@ -221,6 +322,13 @@ var GnomeShell = class {
}
HideMonitorLabelsAsync(params, invocation) {
+ try {
+ this._senderChecker.checkInvocation(invocation);
+ } catch (e) {
+ invocation.return_gerror(e);
+ return;
+ }
+
let sender = invocation.get_sender();
Main.osdMonitorLabeler.hide(sender);
invocation.return_value(null);