Native messaging support in strictly-confined browser snaps

This topic was already discussed in various places (forum posts, bug reports), so this is an attempt to collate and synthesize information in order to facilitate discussion to unblock the situation.

Native messaging is a mechanism implemented in several major browsers (Chromium, Firefox, Brave, Edge, others?) that allows web extensions to communicate with a native application installed on the system.

This mechanism is used by different types of applications to provide better integration between the user’s computer and their browser / web services, for instance password management applications, searching and managing GNOME shell extensions, KDE Plasma integration, digital ID and related services in Belgium, …

This currently doesn’t work in browsers packaged as strictly confined snaps (Chromium, Firefox), because confinement prevents the browser from executing random executables on the user’s host (for obvious security reasons).

Starting with Ubuntu 20.04, the official Chromium package is available only as a strictly confined snap, and Firefox is following suit in the upcoming Ubuntu 22.04, so we need to figure out a way to make native messaging work in those, lest we introduce functional regressions for a significant number of use cases and users. Let’s discuss our options here.

References:

5 Likes

I’m quoting @jamesh on a possible approach to tackle the problem:

The design of the native messaging system for extensions is something that could theoretically fit into a confinement system like snapd or flatpak. Instead of executing the native messaging server directly, the browser could ask something outside the sandbox to execute the server and hand it the communication pipes.

That’s quite a large project though: it’d require modifications to the browser(s), design and implementation of an API (maybe in xdg-desktop-portal?), evaluation of the security implications, etc.

Following on from what @oSoMoN said, the design of the native messaging protocol makes it particularly amenable to cofinement. It was designed for the needs of Chrome, where sandboxed subprocesses running the browser extension needed a well defined method to communicate with its native code component.

Running in-process was out because there would be no way to know if the code would run under the restrictions the browser uses. So instead the native messaging server is run as its own sub-process with length prefixed JSON messages passed on its stdin and stdout.

The native messaging host is described by a JSON metadata blob that looks something like:

{
  "name": "org.gnome.chrome_gnome_shell",
  "description": "Native connector for extensions.gnome.org",
  "path": "/usr/bin/chrome-gnome-shell",
  "type": "stdio",
  "allowed_extensions": [
    "chrome-gnome-shell@gnome.org"
  ]
}

In particular, you have:

  • the name that the web extension requests to talk to
  • the path to the program that will be executed
  • the list of allowed_extensions (for Firefox) or allowed_origins (for Chrome) that the server is willing to talk to

So one possibility would be to represent this as a D-Bus API looking roughly like the following:

<method name="StartNativeMessaging">
  <arg type="s" name="name" direction="in"/>
  <arg type="s" name="extensionOrOrigin" direction="in"/>
  <arg type="h" name="stdin" direction="out"/>
  <arg type="h" name="stdout" direction="out"/>
</method>

When called, the service would look for a matching JSON metadata file and verify that it allows communication with the named extension. If so, it spawns an instance of the app and returns stdin and stdout file descriptors that are pipes to the equivalent descriptors of the app. It may also be necessary to have some kind of process control method too to let the browser terminate the app too.

This could be done as part of snap userd, although we’d want to make sure userd isn’t left needing to reap child processes (something we tried to avoid for the desktop-launch feature). Alternatively it could be done in xdg-desktop-portal, which has a well developed permission prompting system and could also be used by Flatpak packaged browsers (something that could make upstream browser code changes more palatable).

2 Likes

Thanks for the great analysis @jamesh - a D-Bus API sounds like a good approach and would allow easy access via snapd confinement. In terms of the host-side, my preference would be to try and do this via portals so that we get the desktop integration parts “for free”.

I was also thinking whether this could be done via a content slot on each browser snap - but then this would really only work for snaps that want to use native messaging - it wouldn’t work for non-snap applications on the host.

1 Like