1*4882a593SmuzhiyunOverview 2*4882a593Smuzhiyun======== 3*4882a593Smuzhiyun 4*4882a593SmuzhiyunFor general security related questions of perf_event_open() syscall usage, 5*4882a593Smuzhiyunperformance monitoring and observability operations by Perf see here: 6*4882a593Smuzhiyunhttps://www.kernel.org/doc/html/latest/admin-guide/perf-security.html 7*4882a593Smuzhiyun 8*4882a593SmuzhiyunEnabling LSM based mandatory access control (MAC) to perf_event_open() syscall 9*4882a593Smuzhiyun============================================================================== 10*4882a593Smuzhiyun 11*4882a593SmuzhiyunLSM hooks for mandatory access control for perf_event_open() syscall can be 12*4882a593Smuzhiyunused starting from Linux v5.3. Below are the steps to extend Fedora (v31) with 13*4882a593SmuzhiyunTargeted policy with perf_event_open() access control capabilities: 14*4882a593Smuzhiyun 15*4882a593Smuzhiyun1. Download selinux-policy SRPM package (e.g. selinux-policy-3.14.4-48.fc31.src.rpm on FC31) 16*4882a593Smuzhiyun and install it so rpmbuild directory would exist in the current working directory: 17*4882a593Smuzhiyun 18*4882a593Smuzhiyun # rpm -Uhv selinux-policy-3.14.4-48.fc31.src.rpm 19*4882a593Smuzhiyun 20*4882a593Smuzhiyun2. Get into rpmbuild/SPECS directory and unpack the source code: 21*4882a593Smuzhiyun 22*4882a593Smuzhiyun # rpmbuild -bp selinux-policy.spec 23*4882a593Smuzhiyun 24*4882a593Smuzhiyun3. Place patch below at rpmbuild/BUILD/selinux-policy-b86eaaf4dbcf2d51dd4432df7185c0eaf3cbcc02 25*4882a593Smuzhiyun directory and apply it: 26*4882a593Smuzhiyun 27*4882a593Smuzhiyun # patch -p1 < selinux-policy-perf-events-perfmon.patch 28*4882a593Smuzhiyun patching file policy/flask/access_vectors 29*4882a593Smuzhiyun patching file policy/flask/security_classes 30*4882a593Smuzhiyun # cat selinux-policy-perf-events-perfmon.patch 31*4882a593Smuzhiyundiff -Nura a/policy/flask/access_vectors b/policy/flask/access_vectors 32*4882a593Smuzhiyun--- a/policy/flask/access_vectors 2020-02-04 18:19:53.000000000 +0300 33*4882a593Smuzhiyun+++ b/policy/flask/access_vectors 2020-02-28 23:37:25.000000000 +0300 34*4882a593Smuzhiyun@@ -174,6 +174,7 @@ 35*4882a593Smuzhiyun wake_alarm 36*4882a593Smuzhiyun block_suspend 37*4882a593Smuzhiyun audit_read 38*4882a593Smuzhiyun+ perfmon 39*4882a593Smuzhiyun } 40*4882a593Smuzhiyun 41*4882a593Smuzhiyun # 42*4882a593Smuzhiyun@@ -1099,3 +1100,15 @@ 43*4882a593Smuzhiyun 44*4882a593Smuzhiyun class xdp_socket 45*4882a593Smuzhiyun inherits socket 46*4882a593Smuzhiyun+ 47*4882a593Smuzhiyun+class perf_event 48*4882a593Smuzhiyun+{ 49*4882a593Smuzhiyun+ open 50*4882a593Smuzhiyun+ cpu 51*4882a593Smuzhiyun+ kernel 52*4882a593Smuzhiyun+ tracepoint 53*4882a593Smuzhiyun+ read 54*4882a593Smuzhiyun+ write 55*4882a593Smuzhiyun+} 56*4882a593Smuzhiyun+ 57*4882a593Smuzhiyun+ 58*4882a593Smuzhiyundiff -Nura a/policy/flask/security_classes b/policy/flask/security_classes 59*4882a593Smuzhiyun--- a/policy/flask/security_classes 2020-02-04 18:19:53.000000000 +0300 60*4882a593Smuzhiyun+++ b/policy/flask/security_classes 2020-02-28 21:35:17.000000000 +0300 61*4882a593Smuzhiyun@@ -200,4 +200,6 @@ 62*4882a593Smuzhiyun 63*4882a593Smuzhiyun class xdp_socket 64*4882a593Smuzhiyun 65*4882a593Smuzhiyun+class perf_event 66*4882a593Smuzhiyun+ 67*4882a593Smuzhiyun # FLASK 68*4882a593Smuzhiyun 69*4882a593Smuzhiyun4. Get into rpmbuild/SPECS directory and build policy packages from patched sources: 70*4882a593Smuzhiyun 71*4882a593Smuzhiyun # rpmbuild --noclean --noprep -ba selinux-policy.spec 72*4882a593Smuzhiyun 73*4882a593Smuzhiyun so you have this: 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun # ls -alh rpmbuild/RPMS/noarch/ 76*4882a593Smuzhiyun total 33M 77*4882a593Smuzhiyun drwxr-xr-x. 2 root root 4.0K Mar 20 12:16 . 78*4882a593Smuzhiyun drwxr-xr-x. 3 root root 4.0K Mar 20 12:16 .. 79*4882a593Smuzhiyun -rw-r--r--. 1 root root 112K Mar 20 12:16 selinux-policy-3.14.4-48.fc31.noarch.rpm 80*4882a593Smuzhiyun -rw-r--r--. 1 root root 1.2M Mar 20 12:17 selinux-policy-devel-3.14.4-48.fc31.noarch.rpm 81*4882a593Smuzhiyun -rw-r--r--. 1 root root 2.3M Mar 20 12:17 selinux-policy-doc-3.14.4-48.fc31.noarch.rpm 82*4882a593Smuzhiyun -rw-r--r--. 1 root root 12M Mar 20 12:17 selinux-policy-minimum-3.14.4-48.fc31.noarch.rpm 83*4882a593Smuzhiyun -rw-r--r--. 1 root root 4.5M Mar 20 12:16 selinux-policy-mls-3.14.4-48.fc31.noarch.rpm 84*4882a593Smuzhiyun -rw-r--r--. 1 root root 111K Mar 20 12:16 selinux-policy-sandbox-3.14.4-48.fc31.noarch.rpm 85*4882a593Smuzhiyun -rw-r--r--. 1 root root 14M Mar 20 12:17 selinux-policy-targeted-3.14.4-48.fc31.noarch.rpm 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun5. Install SELinux packages from Fedora repo, if not already done so, and 88*4882a593Smuzhiyun update with the patched rpms above: 89*4882a593Smuzhiyun 90*4882a593Smuzhiyun # rpm -Uhv rpmbuild/RPMS/noarch/selinux-policy-* 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun6. Enable SELinux Permissive mode for Targeted policy, if not already done so: 93*4882a593Smuzhiyun 94*4882a593Smuzhiyun # cat /etc/selinux/config 95*4882a593Smuzhiyun 96*4882a593Smuzhiyun # This file controls the state of SELinux on the system. 97*4882a593Smuzhiyun # SELINUX= can take one of these three values: 98*4882a593Smuzhiyun # enforcing - SELinux security policy is enforced. 99*4882a593Smuzhiyun # permissive - SELinux prints warnings instead of enforcing. 100*4882a593Smuzhiyun # disabled - No SELinux policy is loaded. 101*4882a593Smuzhiyun SELINUX=permissive 102*4882a593Smuzhiyun # SELINUXTYPE= can take one of these three values: 103*4882a593Smuzhiyun # targeted - Targeted processes are protected, 104*4882a593Smuzhiyun # minimum - Modification of targeted policy. Only selected processes are protected. 105*4882a593Smuzhiyun # mls - Multi Level Security protection. 106*4882a593Smuzhiyun SELINUXTYPE=targeted 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun7. Enable filesystem SELinux labeling at the next reboot: 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun # touch /.autorelabel 111*4882a593Smuzhiyun 112*4882a593Smuzhiyun8. Reboot machine and it will label filesystems and load Targeted policy into the kernel; 113*4882a593Smuzhiyun 114*4882a593Smuzhiyun9. Login and check that dmesg output doesn't mention that perf_event class is unknown to SELinux subsystem; 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun10. Check that SELinux is enabled and in Permissive mode 117*4882a593Smuzhiyun 118*4882a593Smuzhiyun # getenforce 119*4882a593Smuzhiyun Permissive 120*4882a593Smuzhiyun 121*4882a593Smuzhiyun11. Turn SELinux into Enforcing mode: 122*4882a593Smuzhiyun 123*4882a593Smuzhiyun # setenforce 1 124*4882a593Smuzhiyun # getenforce 125*4882a593Smuzhiyun Enforcing 126*4882a593Smuzhiyun 127*4882a593SmuzhiyunOpening access to perf_event_open() syscall on Fedora with SELinux 128*4882a593Smuzhiyun================================================================== 129*4882a593Smuzhiyun 130*4882a593SmuzhiyunAccess to performance monitoring and observability operations by Perf 131*4882a593Smuzhiyuncan be limited for superuser or CAP_PERFMON or CAP_SYS_ADMIN privileged 132*4882a593Smuzhiyunprocesses. MAC policy settings (e.g. SELinux) can be loaded into the kernel 133*4882a593Smuzhiyunand prevent unauthorized access to perf_event_open() syscall. In such case 134*4882a593SmuzhiyunPerf tool provides a message similar to the one below: 135*4882a593Smuzhiyun 136*4882a593Smuzhiyun # perf stat 137*4882a593Smuzhiyun Error: 138*4882a593Smuzhiyun Access to performance monitoring and observability operations is limited. 139*4882a593Smuzhiyun Enforced MAC policy settings (SELinux) can limit access to performance 140*4882a593Smuzhiyun monitoring and observability operations. Inspect system audit records for 141*4882a593Smuzhiyun more perf_event access control information and adjusting the policy. 142*4882a593Smuzhiyun Consider adjusting /proc/sys/kernel/perf_event_paranoid setting to open 143*4882a593Smuzhiyun access to performance monitoring and observability operations for users 144*4882a593Smuzhiyun without CAP_PERFMON or CAP_SYS_ADMIN Linux capability. 145*4882a593Smuzhiyun perf_event_paranoid setting is -1: 146*4882a593Smuzhiyun -1: Allow use of (almost) all events by all users 147*4882a593Smuzhiyun Ignore mlock limit after perf_event_mlock_kb without CAP_IPC_LOCK 148*4882a593Smuzhiyun >= 0: Disallow raw and ftrace function tracepoint access 149*4882a593Smuzhiyun >= 1: Disallow CPU event access 150*4882a593Smuzhiyun >= 2: Disallow kernel profiling 151*4882a593Smuzhiyun To make the adjusted perf_event_paranoid setting permanent preserve it 152*4882a593Smuzhiyun in /etc/sysctl.conf (e.g. kernel.perf_event_paranoid = <setting>) 153*4882a593Smuzhiyun 154*4882a593SmuzhiyunTo make sure that access is limited by MAC policy settings inspect system 155*4882a593Smuzhiyunaudit records using journalctl command or /var/log/audit/audit.log so the 156*4882a593Smuzhiyunoutput would contain AVC denied records related to perf_event: 157*4882a593Smuzhiyun 158*4882a593Smuzhiyun # journalctl --reverse --no-pager | grep perf_event 159*4882a593Smuzhiyun 160*4882a593Smuzhiyun python3[1318099]: SELinux is preventing perf from open access on the perf_event labeled unconfined_t. 161*4882a593Smuzhiyun If you believe that perf should be allowed open access on perf_event labeled unconfined_t by default. 162*4882a593Smuzhiyun setroubleshoot[1318099]: SELinux is preventing perf from open access on the perf_event labeled unconfined_t. For complete SELinux messages run: sealert -l 4595ce5b-e58f-462c-9d86-3bc2074935de 163*4882a593Smuzhiyun audit[1318098]: AVC avc: denied { open } for pid=1318098 comm="perf" scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=perf_event permissive=0 164*4882a593Smuzhiyun 165*4882a593SmuzhiyunIn order to open access to perf_event_open() syscall MAC policy settings can 166*4882a593Smuzhiyunrequire to be extended. On SELinux system this can be done by loading a special 167*4882a593Smuzhiyunpolicy module extending base policy settings. Perf related policy module can 168*4882a593Smuzhiyunbe generated using the system audit records about blocking perf_event access. 169*4882a593SmuzhiyunRun the command below to generate my-perf.te policy extension file with 170*4882a593Smuzhiyunperf_event related rules: 171*4882a593Smuzhiyun 172*4882a593Smuzhiyun # ausearch -c 'perf' --raw | audit2allow -M my-perf && cat my-perf.te 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun module my-perf 1.0; 175*4882a593Smuzhiyun 176*4882a593Smuzhiyun require { 177*4882a593Smuzhiyun type unconfined_t; 178*4882a593Smuzhiyun class perf_event { cpu kernel open read tracepoint write }; 179*4882a593Smuzhiyun } 180*4882a593Smuzhiyun 181*4882a593Smuzhiyun #============= unconfined_t ============== 182*4882a593Smuzhiyun allow unconfined_t self:perf_event { cpu kernel open read tracepoint write }; 183*4882a593Smuzhiyun 184*4882a593SmuzhiyunNow compile, pack and load my-perf.pp extension module into the kernel: 185*4882a593Smuzhiyun 186*4882a593Smuzhiyun # checkmodule -M -m -o my-perf.mod my-perf.te 187*4882a593Smuzhiyun # semodule_package -o my-perf.pp -m my-perf.mod 188*4882a593Smuzhiyun # semodule -X 300 -i my-perf.pp 189*4882a593Smuzhiyun 190*4882a593SmuzhiyunAfter all those taken steps above access to perf_event_open() syscall should 191*4882a593Smuzhiyunnow be allowed by the policy settings. Check access running Perf like this: 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun # perf stat 194*4882a593Smuzhiyun ^C 195*4882a593Smuzhiyun Performance counter stats for 'system wide': 196*4882a593Smuzhiyun 197*4882a593Smuzhiyun 36,387.41 msec cpu-clock # 7.999 CPUs utilized 198*4882a593Smuzhiyun 2,629 context-switches # 0.072 K/sec 199*4882a593Smuzhiyun 57 cpu-migrations # 0.002 K/sec 200*4882a593Smuzhiyun 1 page-faults # 0.000 K/sec 201*4882a593Smuzhiyun 263,721,559 cycles # 0.007 GHz 202*4882a593Smuzhiyun 175,746,713 instructions # 0.67 insn per cycle 203*4882a593Smuzhiyun 19,628,798 branches # 0.539 M/sec 204*4882a593Smuzhiyun 1,259,201 branch-misses # 6.42% of all branches 205*4882a593Smuzhiyun 206*4882a593Smuzhiyun 4.549061439 seconds time elapsed 207*4882a593Smuzhiyun 208*4882a593SmuzhiyunThe generated perf-event.pp related policy extension module can be removed 209*4882a593Smuzhiyunfrom the kernel using this command: 210*4882a593Smuzhiyun 211*4882a593Smuzhiyun # semodule -X 300 -r my-perf 212*4882a593Smuzhiyun 213*4882a593SmuzhiyunAlternatively the module can be temporarily disabled and enabled back using 214*4882a593Smuzhiyunthese two commands: 215*4882a593Smuzhiyun 216*4882a593Smuzhiyun # semodule -d my-perf 217*4882a593Smuzhiyun # semodule -e my-perf 218*4882a593Smuzhiyun 219*4882a593SmuzhiyunIf something went wrong 220*4882a593Smuzhiyun======================= 221*4882a593Smuzhiyun 222*4882a593SmuzhiyunTo turn SELinux into Permissive mode: 223*4882a593Smuzhiyun # setenforce 0 224*4882a593Smuzhiyun 225*4882a593SmuzhiyunTo fully disable SELinux during kernel boot [3] set kernel command line parameter selinux=0 226*4882a593Smuzhiyun 227*4882a593SmuzhiyunTo remove SELinux labeling from local filesystems: 228*4882a593Smuzhiyun # find / -mount -print0 | xargs -0 setfattr -h -x security.selinux 229*4882a593Smuzhiyun 230*4882a593SmuzhiyunTo fully turn SELinux off a machine set SELINUX=disabled at /etc/selinux/config file and reboot; 231*4882a593Smuzhiyun 232*4882a593SmuzhiyunLinks 233*4882a593Smuzhiyun===== 234*4882a593Smuzhiyun 235*4882a593Smuzhiyun[1] https://download-ib01.fedoraproject.org/pub/fedora/linux/updates/31/Everything/SRPMS/Packages/s/selinux-policy-3.14.4-49.fc31.src.rpm 236*4882a593Smuzhiyun[2] https://docs.fedoraproject.org/en-US/Fedora/11/html/Security-Enhanced_Linux/sect-Security-Enhanced_Linux-Working_with_SELinux-Enabling_and_Disabling_SELinux.html 237*4882a593Smuzhiyun[3] https://danwalsh.livejournal.com/10972.html 238