问题来源
你尝试的命令无法工作的原因是它们只在$PATH
变量中查找可执行文件。首先,让我们测试一下我们的假设。
dummy:~$ mkdir test
dummy:~$ cd test
dummy:~/test$ echo '#!/bin/sh' >test.sh
dummy:~/test$ chmod +x test.sh
dummy:~/test$ cd
dummy:~$ command -v test.sh
dummy:~$ PATH+=:/home/dummy/test/
dummy:~$ command -v test.sh
/home/dummy/test/test.sh
这证实了我之前的陈述。
现在,让我们来看看不同用户的$PATH
是什么样子:
dummy:~$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
dummy:~$ su
root:~
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
因此,为了检查特定用户(在您的问题中,即root)是否可以使用给定的命令,您需要知道他的$PATH
环境变量。
解决方案
Debian上这种环境变量的值通常可以在/etc/profile
和/etc/environment/
文件中找到。没有简单的方法可以从文件中获取这些值。
最基本的解决方案是将已知目录临时添加到您的$PATH
变量中,然后使用command -v
:
dummy~$ OLDPATH=$PATH
dummy~$ PATH=$OLDPATH:/sbin:/usr/sbin/:/usr/local/sbin/
dummy~$ command -v poweroff
/sbin/poweroff
dummy~$ PATH=$OLDPATH
这种解决方案有一个问题:如果你想要可移植性,你不知道应该连接哪些文件夹。在大多数情况下,这种方法应该足够了。
另一种解决方案
相反,你可以编写一个使用 setuid 位 的 脚本 程序来代替。Setuid 位是 Linux 操作系统的一个隐蔽功能,允许程序以其所有者权限执行。因此,你可以编写一个程序,像超级用户那样执行一些命令,只是普通用户也可以运行它。这样,你就可以像 root 一样查看 command -v poweroff
的输出。
不幸的是,使用 shebang 的东西不能具有 setuid 位,所以你不能为此创建一个 shell 脚本,你需要一个 C 程序。这是一个可以完成工作的示例程序:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc <= 1)
{
fprintf(stderr, "No arguments.\n");
return 1;
}
char* prog = argv[1];
int i;
for (i = 0; i < strlen(prog); i ++)
{
if (prog[i] < 'a' || prog[i] > 'z')
{
fprintf(stderr, "%s contains invalid characters (%c), exiting.", prog, prog[i]);
return 1;
}
}
char* command = (char*) malloc(strlen(prog) + 30);
if (!command)
{
fprintf(stderr, "No memory!\n");
return 1;
}
sprintf(command, "bash -cli 'command -v %s'", prog);
int exists = 0;
exists |= system(command) == 0;
if (!exists)
{
setuid(0);
exists |= system(command) == 0;
}
return exists ? 0 : 1;
}
安全提示: 上述版本只进行了非常简单的参数验证(只允许通过与^[a-z]*$
匹配的字符串)。真实的程序应该包括更好的验证。
测试
假设我们将文件保存在test.c
中。我们编译它并添加setuid位:
root:~# gcc ./test.c -o ./test
root:~# chown root:root ./test
root:~# chmod 4755 ./test
请注意,
chown
应该在
chmod
之前执行。通常的
755
模式中的
4
是设置UID位。
现在我们可以将程序作为普通用户进行测试。
dummy:~$ ./test ls; echo $?
alias ls='ls -vhF1 --color=auto --group-directories-first'
0
dummy:~$ ./test blah; echo $?
1
dummy:~$ ./test poweroff; echo $?
/sbin/poweroff
0
最棒的是——它足够便携,在cygwin上使用也没有问题。 :)