使用 Linux 内核 call_usermodehelper 在用户模式下运行用户空间进程
Posted
技术标签:
【中文标题】使用 Linux 内核 call_usermodehelper 在用户模式下运行用户空间进程【英文标题】:Running a userspace process in user mode with Linux kernel 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 ;这些凭据将被内核的提交替换。
【参考方案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;
【讨论】:
以上是关于使用 Linux 内核 call_usermodehelper 在用户模式下运行用户空间进程的主要内容,如果未能解决你的问题,请参考以下文章
当前linux所使用的内核在哪个文件夹,如何看当前使用的内核版本情况。