Bladeren bron

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 6 jaren geleden
bovenliggende
commit
c8c504cd20
2 gewijzigde bestanden met toevoegingen van 21 en 3 verwijderingen
  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
         memory usage can be up to ~8 MiB times this number. The default is the number
         of CPU cores.
         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
         - allow_damaged_files: by default damaged files (where missing chunks were
           replaced with runs of zeros by borg check --repair) are not readable and
           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.
           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.
         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
         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):
     def mount(self, mountpoint, mount_options, foreground=False):
         """Mount filesystem on *mountpoint* with *mount_options*."""
         """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:
         if mount_options:
             options.extend(mount_options.split(','))
             options.extend(mount_options.split(','))
         try:
         try:
@@ -92,6 +97,16 @@ class FuseOperations(llfuse.Operations):
             self.allow_damaged_files = True
             self.allow_damaged_files = True
         except ValueError:
         except ValueError:
             pass
             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)
         llfuse.init(self, mountpoint, options)
         if not foreground:
         if not foreground:
             old_id, new_id = daemonize()
             old_id, new_id = daemonize()