As part of a build process, I want to run the following two commands:
sudo chmod a+r /dev/cpu/*/msr
sudo setcap cap_sys_rawio=ep ./bench
This sets the /dev/cpu/*/msr files exposed by the msr kernel module to world-readable, and sets additional permissions on the ./bench binary (produced as part of the build) that it needs to actually read those files.
The problem is this requires root permissions, hence the sudo.
I'd like something like a setuid root script that does these two specific things, but setuid root scripts are not recommended and disabled on modern Linux.
What are my options here for a straightforward solution?
A solution which works only for the second line (the setcap) is also interesting, because I need this one to run every build, while the chmod only needs to run once per boot.
Achieving the same thing via libcap is actually not that much code:
#include <stdio.h>
#include <sys/capability.h>
int main(int arc, char *argv[]) {
    cap_t c = cap_from_text("cap_sys_rawio=ep");
    int status = cap_set_file("./bench", c);
    cap_free(c);
    if (status)
        perror("attempt failed");
    return status != 0;
}
To compile this (on debian, you'll need to sudo apt-get install libcap-dev; on fedora, sudo dnf install libcap-devel):
$ gcc -o mkcap mkcap.c -lcap
If you just run it as is, it will fail since the program needs to have sufficient privilege to actually add the capability to ./bench:
$ ./mkcap
attempt failed: Operation not permitted
So, you need to make it sufficiently capable itself:
$ sudo /sbin/setcap cap_setfcap=ep ./mkcap
$ ./mkcap
$ echo $?
0
"./bench" binary since, depending on your environment, you might worry that someone could abuse mkcap to give cap_sys_rawio to some other program. Using a full pathname would be less ambiguous.chmod go-x ./mkcap to limit who can run it.basic $ sudo setcap cap_setfcap=ei ./mkcap
basic $ ./mkcap
attempt failed: Operation not permitted
basic $ sudo capsh --inh=cap_setfcap --user=$(whoami) --
enhanced $ ./mkcap
enhanced $ echo $?
0
In the enhanced (capsh shell) layer you are able to raise that capability on binaries that have their file inheritable bit set. This way, the default basic layer shells can't get any privilege out of mkcap. In all other ways, the enhanced shell layer is identical to a basic layer. For example, you can execute builds and pretty much do things as normal. (Use exit to leave the enhanced shell.)
There is a pam_cap module that can also add an inheritable bit to all shells of specific users at login etc time.
You can build a simple C program to use in place of the shell script:
#include <stdio.h>
#include <unistd.h>
int main(void) {
    char *const envp[] = {NULL};
    execle("/sbin/setcap", "setcap", "cap_sys_rawio=ep", "./bench", NULL, envp);
    perror("execle");
    return 1;
}
Notes:
./bench is. You may want to hardcode the absolute path.fork and wait. (Don't use system, as this invokes a shell and defeats the purpose of disallowing setuid scripts!)libcap functions instead, but that would be a bit more complicated.glob to expand /dev/cpu/*/msr like the shell does if you want to do the first part, and then stat and chown to avoid having to exec.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With