Dear andreas,
I figured out why CAP_SYS_MODULE is removed in my environment.
My /sbin/modprobe uses system(3) to create a sub process internally(*1),
and drops the capability.
According to man page (*2), I think this is expected behavior.
*1 kmod-25/libkmod/libkmod-module.c:996
*2 http://man7.org/linux/man-pages/man3/system.3.html
I wrote small program to reproduce this issue.
Could you check and try the attached code?
The step is:
$ gcc check.c -lcap -omain
$ cp main sub # "sub" corresponds to insmod that
causes EPERM
$ sudo chown 0.0 main
$ sudo chmod u+s main # "main" corresponds to nvidia-modprobe
$ ./main
This is what I get:
./main euid: 0 # root privilege
CAP_SYS_MODULE: 1 # has capability
./sub euid: 1000 # lost root privilege (1000 is my uid)
CAP_SYS_MODULE: 0 # the cap. is removed.
Strictly, I use dash as /bin/sh
but CAP_SYS_MODULE is dropped when system() is used.
Could you let me know the result on your system?
Best,
Hiromasa YOSHIMOTO
On 2018年02月06日 21:13, Hiromasa YOSHIMOTO wrote:
Dear andreas,
Thank you for your information. I've just checked CAP_SYS_MODULE
capability.
First, EPERM is returned by finit_module syscall. It is defined
in linux-4.14/kernel/module.c and checks CAP_SYS_MODULE as below;
static int may_init_module(void)
{
if (!capable(CAP_SYS_MODULE) || modules_disabled)
return -EPERM;
return 0;
}
So, I think the required capability is CAP_SYS_MODULE only.
Next, I checked when and where CAP_SYS_MODULE is lost.
I added the code below to both nvidia-modprobe and /sbin/insmod
(/bin/kmod),
which is used called by nvidia-modprobe internally.
cap_t cap = cap_get_proc();
cap_get_flag(cap, CAP_SYS_MODULE, CAP_SET, &value);
fprintf(stderr, "CAP_SYS_MODULE: %d\n", (CAP_SET == value));
cap_free(cap);
From my observations, setuid nvidia-modprobe has CAP_SYS_MODULE already;
additional capability was not needed for nvidia-modprobe.
However, the capability is lost in /bin/kmod.
I'm trying some tricks below, but still struggling.
- /sbin/setcap cap_sys_module+eip nvidia-modprobe
- Added prctl(PR_SET_KEEPCAPS,1) to nvidia-modprobe
Best,
Hiromasa YOSHIMOTO
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/capability.h>
#include <stdlib.h>
static void
show_cap()
{
cap_t cap = cap_get_proc();
cap_flag_value_t value;
cap_get_flag(cap, CAP_SYS_MODULE, CAP_SET, &value);
printf("CAP_SYS_MODULE: %d\n", (CAP_SET == value));
cap_free(cap);
}
int
main(int argc, char *argv[])
{
printf("%s euid: %d\n", argv[0], geteuid());
show_cap();
if (argc==1) {
pid_t pid = fork();
switch (pid) {
case 0:
//execlp("./sub", "./sub", "arg1", NULL);
system("./sub 1");
break;
case -1:
break;
default:
waitpid(pid, NULL, 0);
}
}
return 0;
}