Sun*_*lly 4 c linux ssh linux-capabilities
我的自定义程序使用非root权限执行,用户ID为:uid: 1000 euid: 0
在fork()之后,在子进程execv()中调用以运行SSH客户端服务.由于我在非特权用户下启动程序,当尝试将套接字绑定到设备时,Linux内核执行所有权限检查,导致子程序sock_setbindtodevice()
在检查CAP_NET_RAW
功能时出现故障,如下所示.
我正在考虑的解决方案是首先获得子进程中的root权限,执行特权操作,例如设置所需的功能,然后回退到非root用户.
这里的一个问题是,下拉到非root用户需要什么,因为当执行ssh命令时,我希望生成的DSA/RSA
密钥存储在$HOME/.ssh/known_hosts
而不是root/.ssh/known_hosts
.
请在下面找到以下代码段:
void
global_exec_func (const char *proc_name, const char *proc_path, char **arg_list)
{
pid_t pid;
int status, euid;
struct __user_cap_header_struct cap_header_data;
cap_user_header_t cap_header = &cap_header_data;
struct __user_cap_data_struct cap_data_data;
cap_user_data_t cap_data = &cap_data_data;
pid = fork();
if (pid < 0) {
printf("%% can't fork process %s", proc_name);
return;
}
/*
* Child process.
*/
if (pid == 0) {
euid = geteuid(); /* Storing euid */
/*Gaining root privileges */
if (setuid(0) < 0) {
printf("setuid(0) failed");
}
printf("After setting: getuid: %d geteuid: %d\n", getuid(),
geteuid());
cap_header->pid = 0;
cap_header->version = _LINUX_CAPABILITY_VERSION;
/* Get the capabilities */
if(capget(cap_header, cap_data) < 0) {
printf("failed capget error:%s", strerror(errno));
}
cap_data->effective = (1 << CAP_NET_RAW); /* Set bit 13 */
cap_data->inheritable = 0;
/* Set the capabilities */
if (capset(cap_header, cap_data) < 0) {
printf("failed capset error:%s", strerror(errno));
}
/* Drop back privileges */
if (seteuid(euid) < 0) {
printf("seteuid(euid) failed");
}
printf("After drop: getuid: %d geteuid: %d\n", getuid(), geteuid());
prctl(PR_SET_KEEPCAPS, 1);
execv(proc_path, arg_list);
exit(1);
}
/*
* Parent Process code follows
*/
Result:
[local]linux#ssh 101.1.1.101
After setting: getuid: 0 geteuid: 0
After drop: getuid: 0 geteuid: 0
The authenticity of host '101.1.1.101 (101.1.1.101)' can't be established.
DSA key fingerprint is 0c:61:df:01:93:74:1f:5f:49:34:f4:4e:06:e8:d7:5f.
Are you sure you want to continue connecting (yes/no)? ^C
[local]linux#
Run Code Online (Sandbox Code Playgroud)
结果显示ssh成功但程序root
在此时运行,这是不正确的.我如何回到UID,uid: 1000 euid: 0
以便ssh密钥存储在正确的目录中.
请注意并建议我的解决方案,它真的能解决问题吗?
如果您的程序使用有效的用户ID root执行,那么您具有root权限.
在Linux中,功能分为三组:可继承,允许和有效.可继承定义允许跨越的功能exec()
.允许定义进程允许的功能.有效定义了当前有效的功能.
编辑添加:当包含将要exec()
支持文件系统功能的二进制文件系统时,这些文件系统总是会影响执行过程将具有的功能.查看过程中的功能转换execve()
在男子7个功能手册页.
将进程的所有者或组从root更改为非root时,始终会清除有效的功能集.默认情况下,也会清除允许的功能集,但prctl(PR_SET_KEEPCAPS, 1L)
在身份更改之前调用会告诉内核保持允许的集不变.
因此,要拥有该CAP_NET_RAW
功能,您的程序必须在允许的和有效的集合中拥有它.如果您希望在a CAP_NET_RAW
上保持有效exec()
,则必须将其包含在所有三个功能集中.
编辑添加:如果目标支持文件功能exec()
,则文件功能还必须包含继承和有效集中的那些功能.(仅包括继承和有效集中的功能不授予功能,因为它不在文件功能中的允许集中;但是,如果执行程序具有执行程序,则允许将执行程序中的功能传递给执行者就足够了能力).
您可以使用该setcap
命令为二进制文件授予特定功能.(现在大多数Linux文件系统都支持这些文件功能.)它不需要特权或setuid.只需记住为允许的和有效的集添加所需的功能.
编辑添加一些例子:
格兰特CAP_NET_RAW
对/usr/bin/myprog
(必须不能为setuid或setgid根):
sudo setcap 'cap_net_raw=pe' /usr/bin/myprog
Run Code Online (Sandbox Code Playgroud)
默认情况下,不授予CAP_NET_RAW
到/usr/bin/myprog
,但如果执行有能力(在可继承集和允许集),保留能力(可继承集和允许集,并激活它在有效集):
sudo setcap 'cap_net_raw=ie' /usr/bin/myprog
Run Code Online (Sandbox Code Playgroud)
如果您的程序必须是setuid root,那么您可以使用eg
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#define NEED_CAPS 1
static const cap_value_t need_caps[NEED_CAPS] = { CAP_NET_RAW };
int main(void)
{
uid_t real = getuid();
cap_t caps;
/* Elevate privileges */
if (setresuid(0, 0, 0))
return 1; /* Fatal error, probably not setuid root */
/* Add need_caps to current capabilities. */
caps = cap_get_proc();
if (cap_set_flag(caps, CAP_PERMITTED, NEED_CAPS, need_caps, CAP_SET) ||
cap_set_flag(caps, CAP_EFFECTIVE, NEED_CAPS, need_caps, CAP_SET) ||
cap_set_flag(caps, CAP_INHERITABLE, NEED_CAPS, need_caps, CAP_SET))
return 1; /* Fatal error */
/* Update capabilities */
if (cap_set_proc(caps))
return 1; /* Fatal error */
/* Retain capabilities over an identity change */
if (prctl(PR_SET_KEEPCAPS, 1L))
return 1; /* Fatal error */
/* Return to original, real-user identity */
if (setresuid(real, real, real))
return 1; /* Fatal error */
/* Because the identity changed, we need to
* re-install the effective set. */
if (cap_set_proc(caps))
return 1; /* Fatal error */
/* Capability set is no longer needed. */
cap_free(caps);
/* You now have the CAP_NET_RAW capability.
* It will be retained over fork() and exec().
*/
return 0;
}
Run Code Online (Sandbox Code Playgroud)