更改用户ID以分配其他功能

时间:2013-07-19 09:48:46

标签: 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而不是{{ 1}}。

请在下面找到代码段:

root/.ssh/known_hosts

结果显示ssh成功,但此时程序作为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# 运行,这是不正确的。如何以root的形式返回UID,以便ssh密钥存储在正确的目录中。

请对我的解决方案发表评论并提出建议,是否真的能解决问题?

1 个答案:

答案 0 :(得分:5)

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


在Linux中,capabilities分为三组:可继承,允许和有效。可继承定义exec()中允许的功能。允许定义进程允许的功能。有效定义了当前有效的功能。

编辑添加:当包含将成为exec()的二进制文件的文件系统支持文件系统功能时,这些始终会影响执行过程将具有的功能。请参阅man 7 capabilities手册页中execve() 期间的功能转换。

将进程的所有者或组从root更改为非root时,始终会清除有效的功能集。默认情况下,也会清除允许的功能集,但在身份更改之前调用prctl(PR_SET_KEEPCAPS, 1L)会告知内核保持允许的设置完整。

因此,要拥有CAP_NET_RAW功能,您的程序必须同时拥有允许和有效集。如果您希望CAP_NET_RAWexec()上保持有效,则必须将其包含在所有三个功能集中。

编辑添加:如果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;
}