Launching perf from bash script with CAP

I want to exploit capabilities to run some tests with perf, without running commands as root and without tweaking /proc/sys/kernel/perf_event_paranoid . Some error messages of perf says:

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

so i tried created some bash script with the same source but different sets of capabilities in the following way:

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

Every script has the same source, which is the following:

#!/bin/bash
perf stat -e L1-dcache-load-misses:k seq 1 10

But every script i run gives me the result as if i were a regular user (which means i cannot count kernel events or other privileged stuff). It's like capabilities are discarded when i call the script. perf version is 4.11.ga351e9 .

What is wrong with this method?


Script files often have their suid bit disabled (both in kernel and in some shell interpreters), seems there is similar effect on capabilities (and script files are actually started using interpreters like bash ./scriptfile , so capabilities from the script file may not be inherited by the process):

  • https://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts
  • https://unix.stackexchange.com/questions/87348/capabilities-for-a-script-on-linux
  • Use small simple compiled program to call perf with exec/execve and set capabilities on binary ELF.

    Actual code in Linux kernel for script starting - http://elixir.free-electrons.com/linux/v4.10/source/fs/binfmt_script.c - uses interpreter binary file, not the script file (old bprm->file ), to get permissions like 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;
    ...
    

    I figured out a turnaround taking advantage @osgx suggestion.

    I wrote this small C program which wraps perf. Here it is:

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

    Let's call the executable generated from the code above perf_wrapper . It is compiled with libcap (add the option -lcap to gcc ). The executable simply set MY_CAPABILITY as the capability of the process and then runs perf (or other commands). But this is not sufficient to run perf with CAP_SYS_ADMIN because the executable of perf does not have any capability. To let the things work it is also required to add some capabilities to the perf executable.

    The steps are the following:

  • sudo setcap cap_sys_admin+ei /usr/bin/perf , This sets perf capabilities of CAP_SYS_ADMIN to effective and inheritable (also setting permitted won't allow normal users to run perf without capabilities).
  • sudo setcap cap_sys_admin+eip perf_wrapper set the capability of perf_wrapper
  • Now it is possible to use perf with CAP_SYS_ADMIN by executing perf_wrapper and passing params just like perf in normal bash scripts.

    NOTE: i am not an expert of capabilities. I hope that i didn't do big security mistakes.

    链接地址: http://www.djcxy.com/p/80044.html

    上一篇: 错误:perf.data文件没有样本

    下一篇: 用CAP从bash脚本启动perf