用CAP从bash脚本启动perf
我想利用功能来运行一些带有perf的测试,而不需要以root
身份运行命令,也不需要调整/proc/sys/kernel/perf_event_paranoid
。 perf的一些错误消息说:
You may not have permission to collect stats.
Consider tweaking /proc/sys/kernel/perf_event_paranoid,
which controls use of the performance events system by
unprivileged users (without CAP_SYS_ADMIN).
The current value is 2:
-1: Allow use of (almost) all events by all users
>= 0: Disallow raw tracepoint access by users without CAP_IPC_LOCK
>= 1: Disallow CPU event access by users without CAP_SYS_ADMIN
>= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN
所以我尝试创建一些bash脚本具有相同的来源,但不同的功能集以下列方式:
wrapper_no_cap.sh -> no capabilities set
wrapper_cap_ipc_lock.sh -> setcap cap_ipc_lock+eip ./wrapper_cap_ipc_lock.sh
wrapper_cap_sys_admin.sh -> setcap cap_sys_admin+eip ./wrapper_cap_sys_admin.sh
每个脚本都有相同的来源,如下所示:
#!/bin/bash
perf stat -e L1-dcache-load-misses:k seq 1 10
但是我运行的每个脚本都会给我一个结果,就好像我是一个普通用户(这意味着我无法计算内核事件或其他特权内容)。 这就像我在调用脚本时丢弃的功能。 perf版本是4.11.ga351e9
。
这种方法有什么问题?
脚本文件通常禁用它们的suid位(无论是在内核还是在某些shell解释器中),似乎对功能有类似的影响(并且脚本文件实际上是使用诸如bash ./scriptfile
类的解释器启动的,所以脚本文件中的功能可能不是由进程继承):
使用小型简单编译程序来调用exec / execve执行并设置二进制ELF的功能。
Linux内核中用于脚本启动的实际代码 - http://elixir.free-electrons.com/linux/v4.10/source/fs/binfmt_script.c - 使用解释器二进制文件,而不是脚本文件(旧的bprm->file
),以获得像suid这样的权限
/*
* OK, now restart the process with the interpreter's dentry.
*/
file = open_exec(interp);
if (IS_ERR(file))
return PTR_ERR(file);
bprm->file = file;
retval = prepare_binprm(bprm);
http://elixir.free-electrons.com/linux/v4.10/source/fs/exec.c#L1512
/*
* Fill the binprm structure from the inode.
* Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
*
* This may be called multiple times for binary chains (scripts for example).
*/
int prepare_binprm(struct linux_binprm *bprm)
{
int retval;
bprm_fill_uid(bprm);
/* fill in binprm security blob */
retval = security_bprm_set_creds(bprm);
if (retval)
return retval;
bprm->cred_prepared = 1;
...
static void bprm_fill_uid(struct linux_binprm *bprm)
{
/* Be careful if suid/sgid is set */
inode_lock(inode);
/* reload atomically mode/uid/gid now that lock held */
mode = inode->i_mode;
uid = inode->i_uid;
gid = inode->i_gid;
...
我想出了利用@osgx建议的好处。
我写了这个包装perf的小C程序。 这里是:
#include <unistd.h>
#include <stdio.h>
#include <sys/capability.h>
#define MY_CAPABILIY "cap_sys_admin+eip"
int main(int argc, char * argv[], char * envp[])
{
cap_t old_cap=cap_get_proc(); //save current capabilities
//getting MY_CAPABILITY associated struct
cap_t csa = cap_from_text(MY_CAPABILIY);
//set capabilities
if(cap_set_proc(csa)<0) fprintf(stderr,"cannot set %sn",MY_CAPABILIY);
execve("/usr/bin/perf",argv,envp); //exec perf
//restore capabilties
if(cap_set_proc(old_cap)<0) fprintf(stderr, "Error on capability restoren" );
return 0;
}
我们称之为perf_wrapper
上面的代码生成的可执行文件。 它用libcap编译(将选项-lcap
添加到gcc
)。 可执行文件简单地将MY_CAPABILITY设置为进程的功能,然后运行perf(或其他命令)。 但是,这不足以使用CAP_SYS_ADMIN运行perf,因为perf
的可执行文件没有任何功能。 为了让事情顺利进行,还需要在perf可执行文件中添加一些功能。
步骤如下:
sudo setcap cap_sys_admin+ei /usr/bin/perf
,这将CAP_SYS_ADMIN的perf功能设置为有效且可继承(同样允许设置将不允许普通用户在没有功能的情况下运行perf)。 sudo setcap cap_sys_admin+eip perf_wrapper
设置的能力perf_wrapper
现在可以通过执行perf_wrapper
来使用CAP_SYS_ADMIN中的perf并像通常的bash脚本中的perf一样传递params。
注意:我不是能力专家。 我希望我没有犯大的安全错误。
链接地址: http://www.djcxy.com/p/80043.html上一篇: Launching perf from bash script with CAP
下一篇: Building Perf with Babeltrace (for Perf to CTF Conversion)