Allow passing `/dev/fuse` file descriptor from parent process
authorMattias Nissler <mnissler@chromium.org>
Mon, 27 Aug 2018 13:17:57 +0000 (15:17 +0200)
committerNikolaus Rath <Nikolaus@rath.org>
Tue, 9 Oct 2018 19:36:22 +0000 (20:36 +0100)
commit64e11073b9347fcf9c6d1eea143763ba9e946f70
tree52c725e34474d9a1678a21d174b08f51ba5f1a49
parent4a6a5daec0384aa91ab142b5f6f4e1b3def7160c
Allow passing `/dev/fuse` file descriptor from parent process

This adds support for a mode of operation in which a privileged parent
process opens `/dev/fuse` and takes care of mounting. The FUSE file
system daemon can then run as an unprivileged child that merely
processes requests on the FUSE file descriptor, which get passed using
the special `/dev/fd/%u` syntax for the mountpoint parameter.

The main benefit is that no privileged operations need to be performed
by the FUSE file system daemon itself directly or indirectly, so the
FUSE process can run with fully unprivileged and mechanisms like
securebits and no_new_privs can be used to prevent subprocesses from
re-acquiring privilege via setuid, fscaps, etc. This reduces risk in
case the FUSE file system gets exploited by malicious file system
data.

Below is an example that illustrates this. Note that I'm using shell
for presentation purposes, the expectation is that the parent process
will implement the equivalent of the `mount -i` and `capsh` commands.

```
\# example/hello can mount successfully with privilege
$ sudo sh -c "LD_LIBRARY_PATH=build/lib ./example/hello /mnt/tmp"
$ sudo cat /mnt/tmp/hello
Hello World!
$ sudo umount /mnt/tmp

\# example/hello fails to mount without privilege
$ sudo capsh --drop=all --secbits=0x2f -- -c 'LD_LIBRARY_PATH=build/lib ./example/hello -f /mnt/tmp'
fusermount3: mount failed: Operation not permitted

\# Passing FUSE file descriptor via /dev/fd/%u allows example/hello to work without privilege
$ sudo sh -c '
      exec 17<>/dev/fuse
      mount -i -o nodev,nosuid,noexec,fd=17,rootmode=40000,user_id=0,group_id=0 -t fuse hello /mnt/tmp
      capsh --drop=all --secbits=0x2f -- -c "LD_LIBRARY_PATH=build/lib example/hello /dev/fd/17"
    '
$ sudo cat /mnt/tmp/hello
Hello World!
$ sudo umount /mnt/tmp
```
ChangeLog.rst
lib/fuse_lowlevel.c
lib/helper.c
lib/mount_util.c
lib/mount_util.h