ตรวจสอบ SELinux

Android ขอแนะนำให้ OEM ทดสอบการใช้งาน SELinux อย่างละเอียด เมื่อผู้ผลิตใช้งาน SELinux ควรนำนโยบายใหม่ไปใช้กับกลุ่มอุปกรณ์ทดสอบก่อน

หลังจากใช้นโยบายใหม่แล้ว ให้ตรวจสอบว่า SELinux ทำงานในโหมดที่ถูกต้องในอุปกรณ์โดยใช้คำสั่ง getenforce

คำสั่งนี้จะแสดงโหมด SELinux ทั่วโลก ซึ่งอาจเป็นโหมดบังคับใช้หรือโหมดอนุญาต หากต้องการกำหนดโหมด SELinux สำหรับแต่ละโดเมน คุณต้องตรวจสอบไฟล์ที่เกี่ยวข้อง หรือเรียกใช้เวอร์ชันล่าสุดของ sepolicy-analyze โดยใช้แฟล็ก (-p) ที่เหมาะสม ซึ่งอยู่ใน /platform/system/sepolicy/tools/

อ่านการปฏิเสธ

ตรวจสอบข้อผิดพลาดซึ่งจะถูกกำหนดเส้นทางเป็นบันทึกเหตุการณ์ไปยัง dmesg และ logcat และดูได้ในอุปกรณ์ ผู้ผลิตควรตรวจสอบเอาต์พุต SELinux ไปยัง dmesg ในอุปกรณ์เหล่านี้และปรับแต่งการตั้งค่าก่อนที่จะเผยแพร่ต่อสาธารณะในโหมดอนุญาต และเปลี่ยนเป็นโหมดบังคับใช้ในที่สุด ข้อความบันทึกของ SELinux จะมี avc: จึงค้นหาได้ง่ายด้วย grep คุณสามารถบันทึกบันทึกการปฏิเสธที่กำลังเกิดขึ้นได้โดยเรียกใช้ cat /proc/kmsg หรือบันทึกบันทึกการปฏิเสธจากการบูตครั้งก่อนโดยเรียกใช้ cat /sys/fs/pstore/console-ramoops

ระบบจะจำกัดอัตราข้อความแสดงข้อผิดพลาดของ SELinux หลังจากบูตเสร็จสมบูรณ์เพื่อหลีกเลี่ยงไม่ให้บันทึกมีข้อมูลมากเกินไป หากต้องการให้แน่ใจว่าคุณเห็นข้อความที่เกี่ยวข้องทั้งหมด ให้ปิดใช้การจำกัดอัตรานี้โดยเรียกใช้ adb shell auditctl -r 0

เอาต์พุตนี้ช่วยให้ผู้ผลิตระบุได้ง่ายเมื่อผู้ใช้หรือคอมโพเนนต์ของระบบละเมิดนโยบาย SELinux จากนั้นผู้ผลิตสามารถแก้ไขลักษณะการทำงานที่ไม่ถูกต้องนี้ได้โดยการเปลี่ยนแปลงซอฟต์แวร์ นโยบาย SELinux หรือทั้ง 2 อย่าง

โดยเฉพาะอย่างยิ่ง ข้อความบันทึกเหล่านี้จะระบุว่ากระบวนการใดจะล้มเหลวในโหมดบังคับใช้และเหตุใด ตัวอย่างเช่น

avc: denied  { connectto } for  pid=2671 comm="ping" path="/dev/socket/dnsproxyd"
scontext=u:r:shell:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket

ตีความเอาต์พุตนี้ดังนี้

  • { connectto } ด้านบนแสดงถึงการดำเนินการที่กำลังทำ เมื่อรวมกับ tclass ที่ส่วนท้าย (unix_stream_socket) จะบอกให้คุณทราบคร่าวๆ ว่ามีการดำเนินการใดกับสิ่งใด ในกรณีนี้ มีบางอย่างพยายามเชื่อมต่อกับซ็อกเก็ตสตรีม Unix
  • scontext (u:r:shell:s0) จะบอกบริบทที่เริ่มการดำเนินการ ในกรณีนี้คือบางอย่างที่ทำงานเป็นเชลล์
  • tcontext (u:r:netd:s0) จะบอกบริบทของเป้าหมายการดำเนินการ ในกรณีนี้คือ `unix_stream_socket` ที่เป็นของ netd
  • comm="ping" ที่ด้านบนจะให้คำแนะนำเพิ่มเติมเกี่ยวกับสิ่งที่กำลังทำงานอยู่ ณ เวลาที่สร้างการปฏิเสธ ในกรณีนี้เป็นคำแนะนำที่ดี

อีกตัวอย่างหนึ่ง

adb shell su root dmesg | grep 'avc: '

เอาต์พุต:

<5> type=1400 audit: avc:  denied  { read write } for  pid=177
comm="rmt_storage" name="mem" dev="tmpfs" ino=6004 scontext=u:r:rmt:s0
tcontext=u:object_r:kmem_device:s0 tclass=chr_file

องค์ประกอบหลักจากการปฏิเสธนี้มีดังนี้

  • การดำเนินการ - การดำเนินการที่พยายามจะทำจะไฮไลต์ไว้ในวงเล็บ read write หรือ setenforce
  • ผู้ดำเนินการ - รายการ scontext (บริบทต้นทาง) แสดงถึง ผู้ดำเนินการ ในกรณีนี้คือ Daemon rmt_storage
  • ออบเจ็กต์ - รายการ tcontext (บริบทเป้าหมาย) แสดงถึง ออบเจ็กต์ที่กำลังดำเนินการ ในกรณีนี้คือ kmem
  • ผลลัพธ์ - รายการ tclass (คลาสเป้าหมาย) ระบุประเภทของออบเจ็กต์ที่กำลังดำเนินการ ในกรณีนี้คือ chr_file (อุปกรณ์อักขระ)

ดัมพ์สแต็กของผู้ใช้และเคอร์เนล

ในบางกรณี ข้อมูลที่อยู่ในบันทึกเหตุการณ์ไม่เพียงพอที่จะระบุแหล่งที่มาของการปฏิเสธ การรวบรวมเชนการเรียก ซึ่งรวมถึงเคอร์เนลและพื้นที่ผู้ใช้ มักจะเป็นประโยชน์ในการทำความเข้าใจเหตุผลที่เกิดการปฏิเสธ

เคอร์เนลล่าสุดกำหนดจุดติดตามชื่อ avc:selinux_audited ใช้ simpleperf ของ Android เพื่อเปิดใช้จุดติดตามนี้และบันทึกเชนการเรียก

การกำหนดค่าที่รองรับ

  • ระบบรองรับ Linux Kernel >= 5.10 โดยเฉพาะอย่างยิ่งสาขาหลักของ Android Common Kernel mainline และ android12-5.10 นอกจากนี้ยังรองรับสาขา android12-5.4 ด้วย คุณสามารถใช้ simpleperf เพื่อตรวจสอบว่ามีการกำหนดจุดติดตามในอุปกรณ์หรือไม่: adb root && adb shell simpleperf list | grep avc:selinux_audited สำหรับเคอร์เนลเวอร์ชันอื่นๆ คุณสามารถเลือกคอมมิต dd81662 และ 30969bc ได้
  • คุณควรจะสามารถจำลองเหตุการณ์ที่คุณกำลังดีบักได้ ระบบไม่รองรับเหตุการณ์เวลาบูตโดยใช้ simpleperf แต่คุณอาจยังรีสตาร์ทบริการเพื่อทริกเกอร์เหตุการณ์ได้

บันทึกเชนการเรียก

ขั้นตอนแรกคือการบันทึกเหตุการณ์โดยใช้ simpleperf record

adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"

จากนั้นควรทริกเกอร์เหตุการณ์ที่ทำให้เกิดการปฏิเสธ หลังจากนั้นให้หยุดการบันทึก ในตัวอย่างนี้ การใช้ Ctrl-c ควรจะบันทึกตัวอย่างได้

^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.

สุดท้าย คุณสามารถใช้ simpleperf report เพื่อตรวจสอบ Stacktrace ที่บันทึกไว้ เช่น

adb shell -t "cd /data/local/tmp && su root simpleperf report -g --full-callgraph"
[...]
Children  Self     Command  Pid   Tid   Shared Object                                   Symbol
100.00%   0.00%    dmesg    3318  3318  /apex/com.android.runtime/lib64/bionic/libc.so  __libc_init
       |
       -- __libc_init
          |
           -- main
              toybox_main
              toy_exec_which
              dmesg_main
              klogctl
              entry_SYSCALL_64_after_hwframe
              do_syscall_64
              __x64_sys_syslog
              do_syslog
              selinux_syslog
              slow_avc_audit
              common_lsm_audit
              avc_audit_post_callback
              avc_audit_post_callback

เชนการเรียกด้านบนเป็นเชนการเรียกแบบรวมของเคอร์เนลและพื้นที่ผู้ใช้ ซึ่งจะช่วยให้คุณเห็นโฟลว์ของโค้ดได้ดียิ่งขึ้นโดยเริ่มการติดตามจากพื้นที่ผู้ใช้ลงไปจนถึงเคอร์เนลที่เกิดการปฏิเสธ ดูข้อมูลเพิ่มเติมเกี่ยวกับ simpleperf ได้ที่ ข้อมูลอ้างอิงคำสั่งที่เรียกใช้งานได้ของ Simpleperf

เปลี่ยนเป็นโหมดอนุญาต

คุณสามารถปิดใช้การบังคับใช้ SELinux ด้วย adb ในบิลด์ userdebug หรือ eng หากต้องการทำเช่นนั้น ให้เปลี่ยน ADB เป็นรูทก่อนโดยเรียกใช้ adb root จากนั้นเรียกใช้คำสั่งต่อไปนี้เพื่อปิดใช้การบังคับใช้ SELinux

adb shell setenforce 0

หรือในบรรทัดคำสั่งเคอร์เนล (ระหว่างการเริ่มต้นใช้งานอุปกรณ์)

androidboot.selinux=permissive
androidboot.selinux=enforcing

หรือผ่าน bootconfig ใน Android 12

androidboot.selinux=permissive
androidboot.selinux=enforcing

ใช้ audit2allow

เครื่องมือ audit2allow จะรับการปฏิเสธ dmesg และแปลงเป็นคำสั่งนโยบาย SELinux ที่เกี่ยวข้อง จึงช่วยเร่งการพัฒนา SELinux ได้อย่างมาก

หากต้องการใช้ ให้เรียกใช้คำสั่งต่อไปนี้

adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy

อย่างไรก็ตาม คุณต้องตรวจสอบการเพิ่มที่อาจเกิดขึ้นแต่ละรายการเพื่อดูสิทธิ์ที่มากเกินไป ตัวอย่างเช่น การป้อนการปฏิเสธ rmt_storage ที่แสดงก่อนหน้านี้ลงใน audit2allow จะทำให้เกิดคำสั่งนโยบาย SELinux ที่แนะนำต่อไปนี้

#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };

ซึ่งจะให้สิทธิ์ rmt ในการเขียนหน่วยความจำเคอร์เนล ซึ่งเป็นช่องโหว่ด้านความปลอดภัยที่ชัดเจน โดยส่วนใหญ่แล้ว คำสั่ง audit2allow เป็นเพียงจุดเริ่มต้น หลังจากใช้คำสั่งเหล่านี้แล้ว คุณอาจต้องเปลี่ยนโดเมนต้นทางและป้ายกำกับของเป้าหมาย รวมถึงรวมมาโครที่เหมาะสมเพื่อให้ได้นโยบายที่ดี บางครั้งการปฏิเสธที่กำลังตรวจสอบไม่ควรส่งผลให้เกิดการเปลี่ยนแปลงนโยบายใดๆ แต่ควรเปลี่ยนแอปที่ทำให้เกิดปัญหา