如何递归检查特定用户是否具有对文件夹及其内容的读取权限?

如何递归检查特定用户是否具有对文件夹及其内容的读取权限?

我需要将用户和目录传递给脚本,并让它吐出用户具有读取访问权限的该目录中的文件夹/文件的列表。 MS 有一个名为 AccessChk for Windows 的工具可以执行此操作,但是 Unix 端是否存在类似的工具?我发现一些代码可以对特定文件夹或文件执行此操作,但我需要它来遍历目录。

答案1

长话短说

find "$dir" ! -type l -print0 |
  sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -r'

您需要询问系统用户是否有读取权限。唯一可靠的方法是将有效 uid、有效 gid 和补充 gid 切换为用户的有效 uid、有效 gid 和补充 gid 并使用系统access(R_OK)调用(即使这对某些系统/配置有一些限制)。

更长的故事

让我们考虑一下 $user 需要什么才能具有读取权限/foo/file.txt(假设 和 都不/foo/foo/file.txt符号链接)?

他需要:

  1. 搜索访问/(无需
  2. 搜索访问/foo(无需
  3. 进入/foo/file.txt

您已经可以看到,仅检查 的权限的方法file.txt将不起作用,因为file.txt即使用户没有/或 的搜索权限,它们也可以说是可读的/foo

以及类似的方法:

sudo -u "$user" find / -readable

也不起作用,因为它不会报告用户没有读取访问权限的目录中的文件(因为无法列出其内容find而运行$user),即使他可以读取它们。

如果我们忘记 ACL 或其他安全措施(apparmor、SELinux...)而只关注传统的权限和所有权属性,以获取给定的(搜索或读取)权限,这已经相当复杂且难以用find.

你需要:

  • 如果该文件归您所有,则您需要所有者的许可(或 uid 0)
  • 如果该文件不属于您,但该组属于您,则您需要该组的权限(或具有 uid 0)。
  • 如果它不属于您所有,也不属于您的任何组,那么其他权限适用(除非您的 uid 为 0)。

find语法上,这里以 uid 1 和 gids 1 和 2 的用户为例,即:

find / -type d \
  \( \
    -user 1 \( -perm -u=x -o -prune \) -o \
    \( -group 1 -o -group 2 \) \( -perm -g=x -o -prune \) -o \
    -perm -o=x -o -prune \
  \) ! -type d -o -type l -o \
    -user 1 \( ! -perm -u=r -o -print \) -o \
    \( -group 1 -o -group 2 \) \( ! -perm -g=r -o -print \) -o \
    ! -perm -o=r -o -print

那个梅干用户无权搜索的目录和其他类型的文件(排除符号链接,因为它们不相关),检查读取访问权限。

或者对于从用户数据库检索的任意一个$user及其组成员身份:

groups=$(id -G "$user" | sed 's/ / -o -group /g'); IFS=" "
find / -type d \
  \( \
    -user "$user" \( -perm -u=x -o -prune \) -o \
    \( -group $groups \) \( -perm -g=x -o -prune \) -o \
    -perm -o=x -o -prune \
  \) ! -type d -o -type l -o \
    -user "$user" \( ! -perm -u=r -o -print \) -o \
    \( -group $groups \) \( ! -perm -g=r -o -print \) -o \
    ! -perm -o=r -o -print

最好的方法是以 root 身份下降树并以用户身份检查每个文件的权限。

 find / ! -type l -exec sudo -u "$user" sh -c '
   for file do
     [ -r "$file" ] && printf "%s\n" "$file"
   done' sh {} +

或者与perl

find / ! -type l -print0 |
  sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -r'

或者与zsh

files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
  [ -r $f ] && print -r -- $f
}

这些解决方案依赖于access(2)系统调用。也就是说,我们不是复制系统用于检查访问权限的算法,而是要求系统使用您尝试打开时使用的相同算法(考虑到权限、ACL...)进行检查用于读取的文件,因此是您将获得的最接近可靠解决方案的文件。

现在,所有这些解决方案都尝试识别用户可以打开阅读的文件路径,这与用户可以读取内容的路径不同。要回答这个更普遍的问题,需要考虑以下几点:

  1. $user 可能没有 的读取访问权限/a/b/file,但如果他拥有file(并且具有对 的搜索访问权限/a/b,并且他具有对系统的 shell 访问权限),那么他将能够更改 的权限file并授予自己访问权限。
  2. /a/b如果他拥有但没有搜索权限,情况也是如此。
  3. $user 可能无权访问,/a/b/file因为他没有对/a或 的搜索访问权限/a/b,但该文件可能有一个硬链接/b/c/file,例如,在这种情况下,他可以/a/b/file通过通过其/b/c/file路径打开它来读取 的内容。
  4. 同样的事情与绑定安装。他可能没有搜索权限/a,但/a/b可能有绑定安装/c,这样他就可以file通过其他路径打开阅读/c/file

$user找到能够读取的路径。为了解决1或2,我们不能再依赖系统access(2)调用。我们可以调整我们的find -perm方法,以假设对目录的搜索访问权限,或者在您成为所有者后立即对文件进行读取访问权限:

groups=$(id -G "$user" | sed 's/ / -o -group /g'); IFS=" "
find / -type d \
  \( \
    -user "$user" -o \
    \( -group $groups \) \( -perm -g=x -o -prune \) -o \
    -perm -o=x -o -prune \
  \) ! -type d -o -type l -o \
    -user "$user" -print -o \
    \( -group $groups \) \( ! -perm -g=r -o -print \) -o \
    ! -perm -o=r -o -print

我们可以通过记录设备和 inode 编号或 $user 具有读取权限的所有文件来解决 3 和 4,并报告具有这些 dev+inode 编号的所有文件路径。这次,我们可以使用更可靠的 access(2)基于 - 的方法:

就像是:

find / ! -type l -print0 |
  sudo -u "$user" perl -Mfiletest=access -0lne 'print 0+-r,$_' |
  perl -l -0ne '
    ($w,$p) = /(.)(.*)/;
    ($dev,$ino) = stat$p or next;
    $writable{"$dev,$ino"} = 1 if $w;
    push @{$p{"$dev,$ino"}}, $p;
    END {
      for $i (keys %writable) {
        for $p (@{$p{$i}}) {
          print $p;
        }
      }
    }'

并将两个解决方案合并为:

{ solution1; solution2
} | perl -l -0ne 'print unless $seen{$_}++'

如果您已经阅读了到目前为止的所有内容,那么应该清楚的是,其中的一部分至少只涉及权限和所有权,而不涉及可能授予或限制读取访问权限的其他功能(ACL、其他安全功能...)。当我们分几个阶段处理它时,如果在脚本运行时创建/删除/重命名文件/目录或修改其权限/所有权,例如在拥有数百万个文件的繁忙文件服务器上,则某些信息可能是错误的。

便携性说明

所有这些代码都是标准的(POSIX,Unix for tbit),除了:

  • -print0是一个 GNU 扩展,现在也受到一些其他实现的支持。对于find缺乏对其支持的实现,您可以-exec printf '%s\0' {} +改为使用,并替换-exec sh -c 'exec find "$@" -print0' sh {} +-exec sh -c 'exec find "$@" -exec printf "%s\0" {\} +' sh {} +.
  • perl不是 POSIX 指定的命令,但广泛可用。您需要perl-5.6.0或以上的-Mfiletest=access.
  • zsh不是 POSIX 指定的命令。上面的代码zsh应该适用于 zsh-3 (1995) 及更高版本。
  • sudo不是 POSIX 指定的命令。只要系统配置允许perl以给定用户身份运行,代码就应该适用于任何版本。

答案2

这是获得解决方案的一种简单途径。只需将“testuser”替换为您自己的用户即可。假设您知道如何使用 find 并且可以替换“.”找到与您相关的目录的参数。

x=测试用户;寻找 。 -type f |xargs ls -la | grep $x | | grep $x | awk '{print$9}'

我创建了三个目录深层结构(dir1、dir2、dir3),其中一些文件名为 test1-3.txt。我仅将用户或组作为 testuser 任意分配给某些文件。

[root@myserver tmp]# x=testuser;寻找 。 -type f |xargs ls -la | grep $x | 查询awk '{print$9}'

./dir1/dir2/dir3/test2.txt

./dir1/test2.txt

./dir1/test3.txt

它的关键是“ls”上的递归选项。这是我的测试所针对的结构的示例:

[root@det1svn01n tmp]# ls -laR dir1

目录1:

总计 12

drwxr-xr-x 3 根根 4096 6 月 17 日 14:55 。

drwxrwxrwt 4 根根 4096 六月 17 14:54 ..

drwxr-xr-x 3 testuser testuser 4096 六月 17 14:55 dir2

-rw-r--r-- 1 root root 0 六月 17 14:55 test1.txt

-rw-r--r-- 1 root testuser 0 六月 17 日 14:55 test2.txt

-rw-r--r-- 1 testuser testuser 0 六月 17 日 14:55 test3.txt

目录1/目录2:

总计 12

drwxr-xr-x 3 testuser testuser 4096 6 月 17 日 14:55 。

drwxr-xr-x 3 根根 4096 六月 17 14:55 ..

drwxr-xr-x 2 根 根 4096 六月 17 14:55 dir3

-rw-r--r-- 1 root root 0 六月 17 14:55 test1.txt

-rw-r--r-- 1 root root 0 六月 17 14:55 test2.txt

-rw-r--r-- 1 root root 0 六月 17 14:55 test3.txt

目录1/目录2/目录3:

总计 8

drwxr-xr-x 2 根根 4096 6 月 17 日 14:55 。

drwxr-xr-x 3 testuser testuser 4096 六月 17 14:55 ..

-rw-r--r-- 1 root root 0 六月 17 14:55 test1.txt

-rw-r--r-- 1 root root 0 六月 17 14:55 test2.txt

-rw-r--r-- 1 root root 0 六月 17 14:55 test3.txt

相关内容