我需要将用户和目录传递给脚本,并让它吐出用户具有读取访问权限的该目录中的文件夹/文件的列表。 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
符号链接)?
他需要:
- 搜索访问
/
(无需读) - 搜索访问
/foo
(无需读) - 读进入
/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...)进行检查用于读取的文件,因此是您将获得的最接近可靠解决方案的文件。
现在,所有这些解决方案都尝试识别用户可以打开阅读的文件路径,这与用户可以读取内容的路径不同。要回答这个更普遍的问题,需要考虑以下几点:
- $user 可能没有 的读取访问权限
/a/b/file
,但如果他拥有file
(并且具有对 的搜索访问权限/a/b
,并且他具有对系统的 shell 访问权限),那么他将能够更改 的权限file
并授予自己访问权限。 /a/b
如果他拥有但没有搜索权限,情况也是如此。- $user 可能无权访问,
/a/b/file
因为他没有对/a
或 的搜索访问权限/a/b
,但该文件可能有一个硬链接/b/c/file
,例如,在这种情况下,他可以/a/b/file
通过通过其/b/c/file
路径打开它来读取 的内容。 - 同样的事情与绑定安装。他可能没有搜索权限
/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 t
bit),除了:
-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