【问题标题】:Running a userspace process in user mode with Linux kernel call_usermodehelper使用 Linux 内核 call_usermodehelper 在用户模式下运行用户空间进程
【发布时间】:2020-10-15 03:42:21
【问题描述】:

我尝试使用call_usermodehelper 在 Linux 内核模块中执行用户空间二进制文件。我发现启动的应用程序正在以 root 模式运行。是否可以在用户模式下运行应用程序,例如命名为user1

int alter_uid_gid(uid_t uid, gid_t gid, struct cred *new)
{
//      struct cred *new;
        new = prepare_creds();
        if (!new)
                return -ENOMEM;
        new->uid = new->euid = new->suid = new->fsuid = KUIDT_INIT(uid);
        new->gid = new->egid = new->sgid = new->fsgid = KGIDT_INIT(gid);
        return commit_creds(new);
}

static int init_func(struct subprocess_info *info, struct cred *new)
{
        printk("[%d]\n", current->pid);
        alter_uid_gid(1000, 1000, new);
        return 0;
}

static int user_process_fork(void *data)
{
    struct subprocess_info *sub_info;
    int ret = 0;
    char *path = (char *)data;
    char *argv[] = {path, NULL};
    static char *envp[] = {"HOME=/", "TERM=linux",
        "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL};

    sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC,
            init_func, NULL, NULL);
    if (sub_info == NULL) return -ENOMEM;

    ret = call_usermodehelper_exec(sub_info, UMH_KILLABLE);
    pr_info("%s: ret %d\n", __func__, ret);
    do_exit(0);

    return ret;
}

根据 Milag 的评论,我尝试使用 prepare_creds()commit_creds(new) 更新 init_func() 中的 u[g/e/s]id。在内核日志中,我可以看到current->uid 已更改为1000。但是当我运行ps aux 时,进程仍处于root 模式。知道为什么吗?

【问题讨论】:

  • 您的内核版本是否将 (struct cred *) 传递给您的 init_func()
  • @Milag,我不确定我是否明白你所说的。这是初始化函数int (*init)(struct subprocess_info *info, struct cred *new),作为call_usermodehelper_setup函数中的参数的声明。
  • 看看相关的内核源码;似乎在提供的 init 例程中修改 (struct cred *new) 成员,如您的 init_func() 可能 是可以的,以及在设置 CONFIG_SECURITY_SELINUX 并满足其他条件时可能出现的恐慌情况。
  • 你好@Milag,在init_func()我更新了cred'uid/euid/gid 为1000,但启动的用户空间进程仍处于root模式。我正在使用new = prepare_creds(); new->uid = KUIDT_INIT(uid); commit_creds(new);。你知道为什么吗?
  • 上周的想法是修改传递给您的 init 例程的 (struct cred *) 的成员。不建议分配另一个结构;考虑这个顺序:内核分配+初始化一个struct cred,使用对该结构的引用调用您的初始化例程,然后在您的初始化例程返回之后提交那个结构。在 init 例程中不需要另一个 prep+commit ;这些凭据将被内核的提交替换。

标签: linux linux-kernel kernel linux-device-driver kernel-module


【解决方案1】:

在我阅读了一些内核源代码并在上面发布了 cmets 之后,OP 稍后显示了已开发 kmod 的更新。

简短回答:是的,可以为从 kmod 启动的用户进程设置不同的 ID。

在将一个init 例程传递给call_usermodehelper_setup() 后,相关的内核服务使用(struct cred *) 调用该init 例程;可以在那里更改各种uid和gid成员。更多详情请见call_usermodehelper_exec_async()

关于kmod通用性有相关建议:

  • 为一组默认的uid和gid添加一对#define

  • 添加对模块参数的支持以设置其他uid和gid

  • 在加载模块时可选择提供命令行参数

例如,请参阅this link

【讨论】:

    【解决方案2】:

    根据@Milag 的评论,以下代码使新的用户空间进程以用户模式运行(使用ps aux 进行检查):

    int alter_uid_gid(uid_t uid, gid_t gid, struct cred *new)
    {
            new->uid = new->euid = new->suid = new->fsuid = KUIDT_INIT(uid);
            new->gid = new->egid = new->sgid = new->fsgid = KGIDT_INIT(gid);
            return 0;
    }
    
    static int init_func(struct subprocess_info *info, struct cred *new)
    {
            printk("[%d]\n", current->pid);
            alter_uid_gid(1000, 1000, new);
            return 0;
    }
    
    static int user_process_fork(void *data)
    {
        struct subprocess_info *sub_info;
        int ret = 0;
        char *path = (char *)data;
        char *argv[] = {path, NULL};
        static char *envp[] = {"HOME=/", "TERM=linux",
            "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL};
    
        sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC,
                init_func, NULL, NULL);
        if (sub_info == NULL) return -ENOMEM;
    
        ret = call_usermodehelper_exec(sub_info, UMH_KILLABLE);
        pr_info("%s: ret %d\n", __func__, ret);
        do_exit(0);
    
        return ret;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-23
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      • 2010-10-21
      • 1970-01-01
      • 2016-01-21
      • 1970-01-01
      相关资源
      最近更新 更多