ตรวจสอบ SELinux

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

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

ซึ่งจะแสดงโหมด SELinux ทั่วโลก ซึ่งอาจเป็น "บังคับใช้" หรือ "อนุญาต" หากต้องการระบุโหมด SELinux สำหรับแต่ละโดเมน คุณต้องตรวจสอบไฟล์ที่เกี่ยวข้องหรือเรียกใช้ sepolicy-analyze เวอร์ชันล่าสุดที่มี Flag (-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 (บริบทแหล่งที่มา) แสดงถึงผู้ดําเนินการ ซึ่งในกรณีนี้คือเดรัมน์ rmt_storage
  • ออบเจ็กต์ - รายการ tcontext (บริบทเป้าหมาย) แสดงถึงออบเจ็กต์ที่ดำเนินการ ซึ่งในกรณีนี้คือ kmem
  • ผลลัพธ์ - รายการ tclass (คลาสเป้าหมาย) ระบุประเภทของออบเจ็กต์ที่มีการดำเนินการ ซึ่งในกรณีนี้คือ chr_file (อุปกรณ์อักขระ)

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

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

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

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

  • รองรับเคอร์เนล Linux เวอร์ชันตั้งแต่ 5.10 ขึ้นไป โดยเฉพาะสาขาเคอร์เนลทั่วไปของ Android 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 อาจใช้เพื่อตรวจสอบสแต็กเทรซที่บันทึกไว้ เช่น

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

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

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

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