我之前问过这个问题,但是没有人给出一个直接的答案。我想知道如何通过C代码输入sudo密码。我正在尝试编写一个脚本来执行sudo bash并输入所需的密码。我也知道对密码进行硬编码的风险,但在这种情况下我不介意。
答案 0 :(得分:1)
不。这样做是antipattern。
根据情况有多种选择:
使用gksudo(如果设置了DISPLAY
环境变量)来显示图形提示。
安装要在/usr/share/yourapp/scripts/
或/usr/lib/yourapp/scripts/
中执行的脚本,以及正确的sudo配置,该配置允许使用sudo运行它们而无需在/etc/sudoers.d/yourapp
(或{{ 1}}在没有/etc/sudoers
)
在程序的开头,检查是否为/etc/sudoers.d/
。如果没有,请使用gksudo / sudo重新执行self,以获得root特权。
对于正常操作,您的程序应仅使用执行程序的真实用户的特权。为了以后可以提高特权,“特权”被保存。因此,最初,您的程序将使用以下方式放弃特权:
geteuid() == 0
要重新提升特权,请使用
uid_t uid = getuid();
gid_t gid = getgid();
if (setresgid(gid, gid, 0) == -1 ||
setresuid(uid, uid, 0) == -1) {
/* Failed: no root privileges! */
}
仅将有效身份更改为root(就像setuid二进制文件一样),或者
if (setresgid(gid, 0, 0) == -1 ||
setresuid(uid, 0, 0) == -1) {
/* Failed: no root privileges! */
}
将真实身份和有效身份更改为root。
通常,特权仅针对派生有特权的子从属而提升,之后,主程序完全使用
放弃特权。 if (setresgid(0, 0, 0) == -1 ||
setresuid(0, 0, 0) == -1) {
/* Failed: no root privileges! */
}
仅在父级和子级之间保持套接字对或管道;然后,孩子可以分叉并执行新的流程。 (如果使用Unix域套接字,则父级甚至可以通过辅助消息发送用于新进程的标准流的新描述符。)
使用文件系统功能为程序提供所需的功能,而无需提升其所有特权。
例如,sudo setcap CAP_NET_BIND_SERVICE=pe /usr/bin/yourapp
赋予 if (setresgid(gid, gid, gid) == -1 ||
setresuid(uid, uid, uid) == -1) {
/* Failed. */
}
CAP_NET_BIND_SERVICE功能(允许且有效;未继承),该功能使您的程序可以绑定到任何未使用的TCP / IP和UDP / IP端口,包括1- 1024。有关功能的详细说明,请参见man 7 capabilities。
使用受信任的帮助程序二进制文件(程序)为您充当sudo。如果您将其安装在/usr/bin/yourapp
,您可以添加必要的sudo配置,以允许执行该配置而无需提供密码。另外,您可以将其设为setuid根,或通过文件系统功能为其提供必要的功能。
为避免其他程序利用此帮助程序,必须确保它仅由您的程序执行。一种确保程序创建Unix域套接字对的方法是,在程序中保持一端开放,而在另一端则为helper提供支持。描述符3.在执行任何操作之前,辅助程序会检查尚无任何内容(以避免“管道填充”攻击),并将单个字节写入父级。父级以单个字节进行响应,但其凭据在ancillary message中。凭据包含父级的进程ID。 (不要简单地使用/usr/lib/yourapp/execute
,因为它允许某些攻击;这种套接字方法会在执行检查时验证父对象是否还活着。)最后,使用readlink()
来读取getppid()
伪-symlink,其中PID是凭据辅助消息中的父进程ID。此时,帮助程序应该发送一个字节,并再次接收带有凭据的字节作为辅助消息,以确保父进程仍然相同。
验证过程很复杂,但这是必要的,以避免通过滥用辅助程序来轻松利用root特权。要实现此目的的另一种方法,请查看Apache suEXEC,这是Apache用于以特定用户权限执行CGI程序的帮助程序。
比方说,您对以明智的方式进行操作完全不感兴趣,并坚持使用密码。精细;我要问的是您不要发布这样的代码,或者至少警告您的用户,这是完全不安全的。
这不仅是一个粗略的骇客:它是一个自杀式的行为,类似于将密码放入您的网站的电子邮件签名中,因为您仅将邮件邮寄给应该在第一时间对您的网站具有管理员访问权限的朋友地点。因此,脚枪带有发夹,没有安全性,并用河豚毒素包被的弹药武装。带有一个漂亮的标签,上面有孩子们可读的大字母,上面写着“请和我一起玩!我很安全!”,存放在孩子们的卧室里。
最简单的方法是使用/proc/PID/exe
标志执行sudo
,这会使它从标准输入中读取密码。例如, example.c :
-S
命令管道在写入模式下打开,因此我们可以向其中写入密码。 #define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
FILE *cmd;
int status;
cmd = popen("/usr/bin/sudo -S id -un 2>/dev/null", "w");
if (!cmd) {
fprintf(stderr, "Cannot run sudo: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
fprintf(cmd, "Password\n");
fflush(cmd);
status = pclose(cmd);
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == EXIT_SUCCESS)
fprintf(stderr, "No errors.\n");
else
fprintf(stderr, "Command failed with exit status %d.\n", WEXITSTATUS(status));
} else
if (WIFSIGNALED(status))
fprintf(stderr, "Command killed by signal %d.\n", WTERMSIG(status));
else
fprintf(stderr, "Command failed.\n");
return EXIT_SUCCESS;
}
重定向会隐藏密码提示。
如果密码正确,则上面的命令将以root身份运行时将2>/dev/null
输出的内容(即:id -un
)输出到标准输出,将root
输出为标准错误。
如果密码不正确,sudo将重试几次(因此几秒钟不会发生任何事情),然后程序将向标准错误报告No errors.
,因为Command failed with exit status 1.
会这样做密码不正确时。