Пример #1
0
    def fixperms(p):
        fd = None
        try:

            # if we can't open the file, we just return and let the unlink
            # fail (again) with `EPERM`.
            # A notable case of why open would fail is symlinks; since we
            # want the symlink and not the target we pass the `O_NOFOLLOW`
            # flag, but this will result in `ELOOP`, thus we never change
            # symlinks. This should be fine though since "on Linux, the
            # permissions of an ordinary symbolic link are not used in any
            # operations"; see symlinks(7).
            try:
                fd = os.open(p, os.O_RDONLY | os.O_NOFOLLOW)
            except OSError:
                return

            # The root-only immutable flag prevents files from being unlinked
            # or modified. Clear it, so we can unlink the file-system tree.
            try:
                linux.ioctl_toggle_immutable(fd, False)
            except OSError:
                pass

            # If we do not have sufficient permissions on a directory, we
            # cannot traverse it, nor unlink its content. Make sure to set
            # sufficient permissions up front.
            try:
                os.fchmod(fd, 0o777)
            except OSError:
                pass
        finally:
            if fd is not None:
                os.close(fd)
Пример #2
0
    def can_modify_immutable(path: str = "/var/tmp") -> bool:
        """Check Immutable-Flag Capability

        This checks whether the calling process is allowed to toggle the
        `FS_IMMUTABLE_FL` file flag. This is limited to `CAP_LINUX_IMMUTABLE`
        in the initial user-namespace. Therefore, only highly privileged
        processes can do this.

        There is no reliable way to check whether we can do this. The only
        possible check is to see whether we can temporarily toggle the flag
        or not. Since this is highly dependent on the file-system that file
        is on, you can optionally pass in the path where to test this. Since
        shmem/tmpfs on linux does not support this, the default is `/var/tmp`.
        """

        with tempfile.TemporaryFile(dir=path) as f:
            # First try whether `FS_IOC_GETFLAGS` is actually implemented
            # for the filesystem we test on. If it is not, lets assume we
            # cannot modify the flag and make callers skip their tests.
            try:
                b = linux.ioctl_get_immutable(f.fileno())
            except OSError as e:
                if e.errno in [errno.EACCES, errno.ENOTTY, errno.EPERM]:
                    return False
                raise

            # Verify temporary files are not marked immutable by default.
            assert not b

            # Try toggling the immutable flag. Make sure we always reset it
            # so the cleanup code can actually drop the temporary object.
            try:
                linux.ioctl_toggle_immutable(f.fileno(), True)
                linux.ioctl_toggle_immutable(f.fileno(), False)
            except OSError as e:
                if e.errno in [errno.EACCES, errno.EPERM]:
                    return False
                raise

        return True
Пример #3
0
    def fixperms(p):
        fd = None
        try:
            fd = os.open(p, os.O_RDONLY)

            # The root-only immutable flag prevents files from being unlinked
            # or modified. Clear it, so we can unlink the file-system tree.
            try:
                linux.ioctl_toggle_immutable(fd, False)
            except OSError:
                pass

            # If we do not have sufficient permissions on a directory, we
            # cannot traverse it, nor unlink its content. Make sure to set
            # sufficient permissions up front.
            try:
                os.fchmod(fd, 0o777)
            except OSError:
                pass
        finally:
            if fd is not None:
                os.close(fd)
Пример #4
0
    def test_ioctl_toggle_immutable(self):
        #
        # Test the `ioctl_toggle_immutable()` helper and make sure it works
        # as intended.
        #

        with open(f"{self.vartmpdir.name}/immutable", "x") as f:
            # Check the file is mutable by default and if we clear it again.
            assert not linux.ioctl_get_immutable(f.fileno())
            linux.ioctl_toggle_immutable(f.fileno(), False)
            assert not linux.ioctl_get_immutable(f.fileno())

            # Set immutable and check for it. Try again to verify with flag set.
            linux.ioctl_toggle_immutable(f.fileno(), True)
            assert linux.ioctl_get_immutable(f.fileno())
            linux.ioctl_toggle_immutable(f.fileno(), True)
            assert linux.ioctl_get_immutable(f.fileno())

            # Verify immutable files cannot be unlinked.
            with self.assertRaises(OSError):
                os.unlink(f"{self.vartmpdir.name}/immutable")

            # Check again that clearing the flag works.
            linux.ioctl_toggle_immutable(f.fileno(), False)
            assert not linux.ioctl_get_immutable(f.fileno())

            # This time, check that we actually set the same flag as `chattr`.
            subprocess.run(
                ["chattr", "+i", f"{self.vartmpdir.name}/immutable"],
                check=True)
            assert linux.ioctl_get_immutable(f.fileno())

            # Same for clearing it.
            subprocess.run(
                ["chattr", "-i", f"{self.vartmpdir.name}/immutable"],
                check=True)
            assert not linux.ioctl_get_immutable(f.fileno())

            # Verify we can unlink the file again, once the flag is cleared.
            os.unlink(f"{self.vartmpdir.name}/immutable")