更改用户ID以分配其他function

问题陈述:我的自定义程序以非root权限执行,其用户标识为: uid: 1000 euid: 0 ,其中fork()后,subprocessexecv()被调用来运行SSH客户端服务。 由于我是非root用户,而套接字绑定到设备Linux内核将执行所有的权限检查,导致子例程sock_setbindtodevice()同时检查CAP_NET_RAWfunction,如下所示。

解决scheme我一直在考虑在subprocess中先获得root权限,然后执行权限操作,比如设置所需的权限并退回到非root用户。

这里可能的问题可能是什么,需要下降到非root用户:执行ssh命令时,其生成的DSA/RSA密钥需要存储在$HOME/.ssh/known_hosts而不是root/.ssh/known_hosts

请find下面的代码片段,

 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# 

结果告诉SSH是成功的,但是我的root目前还不正确。 我怎样才能回到uid: 1000 euid: 0以便ssh密钥存储在正确的目录中。

请对我的解决scheme进行评论和build议,是否真的解决了这个问题。

提前致谢!

如果您的程序使用有效的用户ID root执行,那么您拥有root权限。


在Linux中, 功能分为三组:可继承,允许和有效。 可继承定义了exec()允许哪些功能。 允许定义哪些功能被允许进程。 有效定义哪些功能目前正在生效。

编辑添加:当包含二进制文件系统将是exec() 'd确实支持文件系统功能,这些总是影响执行过程将具有什么功能。 请参阅man 7功能手册页中的execve()中的功能转换 。

将进程的所有者或组从root更改为非root时,始终清除有效功能集。 默认情况下,允许的能力集也被清除,但是在标识改变之前调用prctl(PR_SET_KEEPCAPS, 1L) ,告诉内核保持允许的集合不变。

因此,要拥有CAP_NET_RAW功能,您的程序必须在允许的和有效的集合中拥有它。 如果您希望CAP_NET_RAW通过exec()继续有效,则必须将其包含在所有三个功能集中。

编辑添加:如果exec()的目标支持文件功能,则文件功能还必须在继承的有效集中包含这些功能。 (只有在继承的和有效的集合中包含这个能力才会授予这个能力,因为它不在文件能力允许的范围之内;但是,如果执行者有能力允许将执行者的能力传递给执行者就足够了能力)。


您可以使用setcap命令将特定功能授予二进制文件。 (现在大多数Linux文件系统支持这些文件功能 。)它不需要是特权或setuid。 只要记住将所需的功能添加到允许和有效的集合。

编辑添加一些例子:

CAP_NET_RAW授予/usr/bin/myprog (不能是setuid或setgid root):

 sudo setcap 'cap_net_raw=pe' /usr/bin/myprog 

默认情况下,不要将CAP_NET_RAW授予/usr/bin/myprog ,但是如果执行程序具有(在可继承和允许的集合中)的能力,则保留该能力(在可继承和允许的集合中,并在有效集合中激活它):

 sudo setcap 'cap_net_raw=ie' /usr/bin/myprog 

如果你的程序必须是setuid root,那么你可以使用例如

 #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; }