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

Reply via email to