Пример #1
0
    def test_exact_dir(self):
        self.fs = FilesystemPolicy([ExactDir('/etc')])

        self.checkTrue('/etc')

        self.checkFalse('/')
        self.checkFalse('/etc/passwd')
        self.checkFalse('/etc/shadow')
Пример #2
0
class CheckerTest(unittest.TestCase):
    def test_exact_dir(self):
        self.fs = FilesystemPolicy([ExactDir('/etc')])

        self.checkTrue('/etc')

        self.checkFalse('/')
        self.checkFalse('/etc/passwd')
        self.checkFalse('/etc/shadow')

    def test_recursive_dir(self):
        self.fs = FilesystemPolicy([RecursiveDir('/usr')])

        self.checkTrue('/usr')
        self.checkTrue('/usr/lib')
        self.checkTrue('/usr/lib/a/b/c/d/e')

        self.checkFalse('/')
        self.checkFalse('/etc')
        self.checkFalse('/us')
        self.checkFalse('/usr2')

    def test_exact_file(self):
        self.fs = FilesystemPolicy([ExactFile('/etc/passwd')])

        self.checkTrue('/etc/passwd')

        self.checkFalse('/')
        self.checkFalse('/etc')
        self.checkFalse('/etc/p')
        self.checkFalse('/etc/passwd2')

    def test_path_checks(self):
        self.fs = FilesystemPolicy([])

        self.assertRaises(AssertionError, self.check, '')
        self.assertRaises(AssertionError, self.check, 'not/an/absolute/path')
        self.assertRaises(AssertionError, self.check, '/usr/lib/not/./a/../normalized/path')

    def test_rule_type_check(self):
        self.assertRaises(AssertionError, ExactFile, '/usr/lib')
        self.assertRaises(AssertionError, ExactDir, '/etc/passwd')
        self.assertRaises(AssertionError, RecursiveDir, '/etc/passwd')

    def test_bad_path_check(self):
        self.assertRaises(AssertionError, ExactFile, 'not/an/absolute/path')
        self.assertRaises(AssertionError, ExactDir, '/nota/./normalized/path')
        self.assertRaises(AssertionError, RecursiveDir, '')

    def check(self, path):
        self.fs.check(path)

    def checkTrue(self, path):
        self.assertTrue(self.fs.check(path))

    def checkFalse(self, path):
        self.assertFalse(self.fs.check(path))
Пример #3
0
    def test_exact_file(self):
        self.fs = FilesystemPolicy([ExactFile('/etc/passwd')])

        self.checkTrue('/etc/passwd')

        self.checkFalse('/')
        self.checkFalse('/etc')
        self.checkFalse('/etc/p')
        self.checkFalse('/etc/passwd2')
Пример #4
0
    def test_recursive_dir(self):
        self.fs = FilesystemPolicy([RecursiveDir('/usr')])

        self.checkTrue('/usr')
        self.checkTrue('/usr/lib')
        self.checkTrue('/usr/lib/a/b/c/d/e')

        self.checkFalse('/')
        self.checkFalse('/etc')
        self.checkFalse('/us')
        self.checkFalse('/usr2')
Пример #5
0
    def _access_check(self, debugger: Debugger, file: str,
                      fs_jail: FilesystemPolicy) -> None:
        # We want to ensure that if there are symlinks, the user must be able to access both the symlink and
        # its destination. However, we are doing path-based checks, which means we have to check these as
        # as normalized paths. normpath can normalize a path, but also changes the meaning of paths in presence of
        # symlinked directories etc. Therefore, we compare both realpath and normpath and ensure that they refer to
        # the same file, and check the accessibility of both.
        #
        # This works, except when the child process uses /proc/self, which refers to something else in this process.
        # Therefore, we "project" it by changing it to /proc/[tid] for computing the realpath and doing the samefile
        # check. However, we still keep it as /proc/self when checking access rules.

        # normpath doesn't strip leading slashes
        projected = normalized = '/' + os.path.normpath(file).lstrip('/')
        if normalized.startswith('/proc/self'):
            file = os.path.join(f'/proc/{debugger.tid}',
                                os.path.relpath(file, '/proc/self'))
            projected = '/' + os.path.normpath(file).lstrip('/')
        elif normalized.startswith(f'/proc/{debugger.tid}/'):
            # If the child process uses /proc/getpid()/foo, set the normalized path to be /proc/self/foo.
            # Access rules can more easily check /proc/self.
            normalized = os.path.join(
                '/proc/self', os.path.relpath(file, f'/proc/{debugger.tid}'))
        real = os.path.realpath(file)

        try:
            same = normalized == real or os.path.samefile(projected, real)
        except OSError:
            raise DeniedSyscall(
                ACCESS_ENOENT,
                f'Cannot stat, file: {file}, projected: {projected}, real: {real}'
            )

        if not same:
            raise DeniedSyscall(
                ACCESS_EACCES,
                f'Suspected symlink trickery, file: {file}, projected: {projected}, real: {real}'
            )

        if not fs_jail.check(normalized):
            raise DeniedSyscall(ACCESS_EACCES,
                                f'Denying {file}, normalized to {normalized}')

        if normalized != real:
            proc_dir = f'/proc/{debugger.tid}'
            if real.startswith(proc_dir):
                real = os.path.join('/proc/self',
                                    os.path.relpath(real, proc_dir))

            if not fs_jail.check(real):
                raise DeniedSyscall(ACCESS_EACCES,
                                    f'Denying {file}, real path {real}')
Пример #6
0
 def _compile_fs_jail(
         self, fs: Sequence[FilesystemAccessRule]) -> FilesystemPolicy:
     return FilesystemPolicy(fs)
Пример #7
0
    def test_path_checks(self):
        self.fs = FilesystemPolicy([])

        self.assertRaises(AssertionError, self.check, '')
        self.assertRaises(AssertionError, self.check, 'not/an/absolute/path')
        self.assertRaises(AssertionError, self.check, '/usr/lib/not/./a/../normalized/path')
Пример #8
0
 def _compile_fs_jail(self, fs):
     return FilesystemPolicy(fs or [])