Bringing XDG Desktop Portal support to Emacs
Sandboxes, like those of bwrap
and systemd
, are an effective way to secure
your system from applications you don't or can't trust.
I may trust Emacs, but I don't trust myself to audit every single line of code (built-in or third-party) that the Emacs Lisp interpreter evaluates. Additionally, I may trust in the security of Chromium's provided sandbox, but I don't trust that it's infallible; no software is.
I do however put great effort to keep things secure which is why the most
vulnerable parts of my system, the applications which are first and foremost a
presentation layer over other people's code, are sandboxed either via bwrap
if
an executable or systemd
if a daemon.
Sandboxing Emacs and reducing its attack vector to an acceptable level has been
a very tedious and error-prone process for me, here's an excerpt from my
emacs.service
file constituting the unit's security policy:
[Service] # ... SystemCallArchitectures=native LockPersonality=yes ProtectSystem=strict ProtectClock=yes ProtectHostname=yes ProtectProc=invisible ProtectKernelTunables=yes ProtectKernelModules=yes ProtectKernelLogs=yes ProtectControlGroups=yes PrivateUsers=yes PrivateDevices=yes PrivateTmp=yes RestrictNamespaces=yes RestrictSUIDSGID=yes NoNewPrivileges=yes CapabilityBoundingSet=
Pairing this with a bwrap
-sandboxed qutebrowser
breaks the most ubiquitious
functionality across Emacs: following links. When Emacs attempts to follow a
link, it's not directly executing the qutebrowser
binary with the URL as an
argument, instead it calls a script that is silently shadowing it.
Shadowing a program is achieved by creating a script with an identical name as the original program, but storing it in a path preceeding that of the original program.
Emacs can't spawn a qutebrowser
process because the unit's security policy
prohibits it from spawning a nested sandbox. So, what are we supposed to do
then?
It's quite easy actually, the Freedesktop/Flatpak team have already encountered this problem and worked out a thorough and well-architectured solution known as xdg-desktop-portal.
What we require is the OpenURI
method from the org.freedesktop.portal.OpenURI
interface which forwards a given URI to its associated application. Sounds good,
Emacs has excellent D-Bus support, we first need a function that forwards the
URI 1 to the portal:
(defun browse-url-xdg-desktop-portal (url &rest args) "Open URL via a portal backend." (dbus-call-method :session "org.freedesktop.portal.Desktop" "/org/freedesktop/portal/desktop" "org.freedesktop.portal.OpenURI" "OpenURI" "" url '(:array :signature "{sv}")))
And to then set the default browser function to it:
(setopt browse-url-browser-function #'browse-url-xdg-desktop-portal)
Opening links should now work seamlessly across Emacs.
Footnotes
Despite there being a technical difference, I've used URI and URL interchangeably, but only to remain consistent with the naming conventions of the APIs in question.