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