基本上,您将依赖于评估程序安全性的专家,他们对允许提升权限的程序可以做什么有深入的了解。
上面这句话包括程序使用的库和您正在使用的系统(Linux?*BSD?)的知识。
可能不需要进行完整的源代码审核。对于文档齐全的程序,完整阅读文档可能足以找到不安全的选项。它还取决于您可用的时间和专业知识。
通常情况下,证明程序不安全通常很容易显示(一旦发现),但证明它是安全的将非常困难。
主要的问题点是:
- 不适合您的用例的功能。从在 shell 中查看的选项到现在允许嵌入式 LUA 的配置文件。
- 程序中的漏洞。交互式程序中的缓冲区溢出通常并不重要,但当以 root 身份运行时,它会成为特权提升的途径。
- 使用许多或过于复杂的库。例如图形程序。你会想马上拒绝这些。
您将希望允许这些用户使用尽可能简单和“封闭”的 sudo 程序运行。如果程序允许多个选项,则提供它们以使其固定,如果有配置文件,则使其使用用户不可配置的文件,等等。
我建议允许使用有限的 shell 脚本或短程序,而不是成熟的程序。
例如,对于“不受信任的用户需要绝对读取任何文件”(或 emacs、vi……)的问题,我可能会使用:
/**
* This program when run by sudo allows reading as root an arbitrary file via less(1)
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int sudo_var(const char* varname) {
char *value, *endptr;
int number;
value = getenv(varname);
if (!value) {
fprintf(stderr, "Environment doesn't contain %s!\n", varname);
exit(5);
}
errno = 0;
number = strtol(value, &endptr, 10);
if (*endptr != '\0') {
fprintf(stderr, "Bad environment variable %s!\n", varname);
exit(5);
}
if (errno || number <= 0) {
fprintf(stderr, "Bad value for environment variable %s!\n", varname);
exit(5);
}
return number;
}
int main(int argc, char **argv, char **envp) {
gid_t gid;
uid_t uid;
int fd, err;
struct stat stat_variable;
char *less_argv[] = {"/usr/bin/less", NULL};
if (argc < 2) {
fprintf(stderr, "Provide the file to read\n");
return 1;
}
fd = open(argv[1], O_RDONLY | O_NOCTTY);
if (fd == -1) {
perror("Error opening file");
return 2;
}
err = fstat(fd, &stat_variable);
if (err) {
perror("stat error");
return 3;
}
if (!S_ISREG(stat_variable.st_mode)) {
fprintf(stderr, "Not a regular file\n");
return 3;
}
err = dup2(fd, STDIN_FILENO);
if (err) {
perror("dup2 failed");
return 4;
}
gid = sudo_var("SUDO_GID");
uid = sudo_var("SUDO_UID");
err = setgid(gid);
if (err) {
perror("Error setting group id");
return 6;
}
err = setuid(uid);
if (err) {
perror("Error setting user id");
return 7;
}
execve(less_argv[0], less_argv, envp);
perror("exec failed");
return 8;
}