考虑以下说明我的问题的简化示例。
服务器的操作系统是 Debian 11 armhf。
我们在目录中/botm/test/
:
drwx------ 2 b b 4096 Sep 27 19:27 hide
-rwsr-xr-x 1 b b 8260 Sep 27 19:54 test1
-rwsr-xr-x 1 b b 8264 Sep 27 19:58 test2
-rw-r--r-- 1 b b 56 Sep 27 19:52 test1.awk
-rw-r--r-- 1 b b 172 Sep 27 19:54 test1.c
-rw-r--r-- 1 b b 186 Sep 27 19:58 test2.c
可以看出,只有所有者b
才能访问该目录/botm/test/hide
。
我们现在是用户test
我们无法访问该文件/botm/test/hide/test.txt
:
$ cat hide/test.txt
cat: hide/test.txt: Permission denied
好的。程序设置test1
了test2
SETUID 位。
所以即使我们是用户test
,我们也可以像用户一样运行这些程序b
。
首先,test2.c
:
/* test2.c */
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int r;
r = execl("/usr/bin/cat","/usr/bin/cat","/botm/test/hide/test.txt", (char *)0);
printf("%d\n",r);
return r;
}
$ ./test2
hidden content
$
正如预期的那样,它有效。
现在, test1.c
, test1.awk
:
/* test1.c */
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int r;
r = execl("/usr/bin/mawk","/usr/bin/mawk","-f","/botm/test/test1.awk", (char *)0);
printf("%d\n",r);
return r;
}
# test1.awk
BEGIN {
system("cat /botm/test/hide/test.txt");
};
$ ./test1
cat: /botm/test/hide/test.txt: Permission denied
$
令人惊讶的是,这不起作用。
我的期望:
如果我运行test1
具有 SETUID 的程序,那么它应该像 user 一样运行b
。
因此,如果程序调用/usr/bin/mawk
,那么它也应该以 user 身份运行b
。
因此,如果 awk 脚本随后调用cat
,那么cat
也应该以用户身份运行b
。
因此cat
应该能够访问/botm/test/hide/test.txt
仅对 user 可用的内容b
。
但这不会发生。
现在有一些真实的背景。
以上只是一个简化的例子。我有一个包含多个 C 和 AWK 程序的项目。从 C 程序调用 AWK 程序来处理一些文本并生成一些输出。这包括有时调用cat
或其他系统工具。其中一些程序作为用户从 Apache 服务器运行,该用户www-data
无权(也不应该拥有)这些程序创建的某些临时文件。这就是使用SETUID 的原因。这种工作流程在这个项目中被大量使用(从 2014 年开始)。
我现在将它从我的旧服务器(它工作的地方)移动到一个新的服务器(它不起作用的地方)。将项目更改为不依赖此机制将是一个非常大的重新设计,我现在不想这样做。
为什么当 C 程序(带有 SETUID)调用调用系统工具(cat
,...)的 AWK 脚本时,SETUID 不被保留?
怎么做才能再次保存?
在以前版本的系统上它可以工作。
编辑添加:
我验证了 AWK 程序仍然可以访问隐藏文件,只有调用的东西system()
没有。
所以我发现这种行为的原因是在使用system()
(或getline
使用管道)时mawk
调用sh
执行命令。
AWK 没有exec
或类似的东西。
sh
除非使用-p
.
这是新的行为。这部分man sh
在我的旧服务器上不存在:
-p priviliged Do not attempt to reset effective uid if it does not match uid. This is not set by default to help avoid incorrect usage by
setuid root programs via system(3) or popen(3).
因此,要使我的程序再次运行,我必须至少实现以下其中一项:
- 默认情况下
/usr/bin/sh
不删除SETUID。 - 使用for时使用make
mawk
或其他兼容的 AWK 解释器-p
/usr/bin/sh
system()
- 找到从 AWK 运行程序的不同方式
- 重新设计所有程序以不依赖于此。例如,在 C、Perl 或 Python 中执行它们,它们确实具有不依赖于
sh
.
重新设计整个项目(以及其他一些也使用此工作流程的项目)将花费我目前没有的精力和时间,因此我真的需要以某种方式恢复完整的 SETUID 功能。
实际上,可以在 AWK 程序中轻松重新创建 的功能,但在我的项目中cat
也可以调用其他工具: , , , , , , . 在大多数情况下,不使用 SETUID 调用它们是不可接受的。system()
[ -f
cat
cp
mkdir
mv
sleep
wget
在对类似问题的回答中:
https
://unix.stackexchange.com/a/565254/543092 有一个建议可以使用
setresuid()
。因为在我的项目中,AWK 脚本总是从 C 程序中调用,这是我可以使用的解决方案。
我做这样的事情:
这样“伪装”就完成了,并且
sh
不会知道之前有一个SETUID。一切正常。这是一个更好的解决方案,而不是
sh
总是表现得好像有-p
. 如果我知道自己在做什么,它将保留我的 SETUID。我使用
setreuid()
而不是setresuid()
那里建议的,因为:setreuid()
足够的setresuid()
是一个 GNU 扩展。