Ver Fonte

security fix: configure FUSE with "default_permissions", fixes #3903

"default_permissions" is now enforced by borg by default to let the
kernel check uid/gid/mode based permissions.

"ignore_permissions" can be given to not enforce "default_permissions".

note: man mount.fuse explicitly tells about the security issue:

    default_permissions
	By  default FUSE doesn't check file access permissions, ...
	This option enables permission checking, restricting access
	based on file mode.
	This option is usually useful together with the allow_other
	mount option.

We consider this a pitfall waiting for someone to fall into and this is
why we chose to change the default behaviour for borg.

(cherry picked from commit 1b277cb1ff5c299d3431e40d01af16e02c5f0115)
Thomas Waldmann há 6 anos atrás
pai
commit
c8c504cd20
2 ficheiros alterados com 21 adições e 3 exclusões
  1. 5 2
      borg/archiver.py
  2. 16 1
      borg/fuse.py

+ 5 - 2
borg/archiver.py

@@ -1613,12 +1613,15 @@ class Archiver:
         memory usage can be up to ~8 MiB times this number. The default is the number
         of CPU cores.
 
-        For mount options, see the fuse(8) manual page. Additional mount options
-        supported by borg:
+        For FUSE configuration and mount options, see the mount.fuse(8) manual page.
 
+        Additional mount options supported by borg:
         - allow_damaged_files: by default damaged files (where missing chunks were
           replaced with runs of zeros by borg check --repair) are not readable and
           return EIO (I/O error). Set this option to read such files.
+        - ignore_permissions: for security reasons the "default_permissions" mount
+          option is internally enforced by borg. "ignore_permissions" can be given
+          to not enforce "default_permissions".
 
         When the daemonized process receives a signal or crashes, it does not unmount.
         Unmounting in these cases could cause an active rsync or similar process

+ 16 - 1
borg/fuse.py

@@ -84,7 +84,12 @@ class FuseOperations(llfuse.Operations):
 
     def mount(self, mountpoint, mount_options, foreground=False):
         """Mount filesystem on *mountpoint* with *mount_options*."""
-        options = ['fsname=borgfs', 'ro']
+        # default_permissions enables permission checking by the kernel. Without
+        # this, any umask (or uid/gid) would not have an effect and this could
+        # cause security issues if used with allow_other mount option.
+        # When not using allow_other or allow_root, access is limited to the
+        # mounting user anyway.
+        options = ['fsname=borgfs', 'ro', 'default_permissions']
         if mount_options:
             options.extend(mount_options.split(','))
         try:
@@ -92,6 +97,16 @@ class FuseOperations(llfuse.Operations):
             self.allow_damaged_files = True
         except ValueError:
             pass
+        try:
+            options.remove('ignore_permissions')
+            # if above does not raise ValueError (meaning: ignore_permissions is present),
+            # we remove default_permissions again.
+            # in case users have a use-case that requires NOT giving "default_permissions",
+            # this is enabled by the custom "ignore_permissions" mount option which just
+            # removes "default_permissions" again:
+            options.remove('default_permissions')
+        except ValueError:
+            pass
         llfuse.init(self, mountpoint, options)
         if not foreground:
             old_id, new_id = daemonize()