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