กำลังตรวจสอบความถูกต้องของ 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 หรือทั้งสองอย่าง

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

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

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

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

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

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

  • 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 เพื่อตรวจสอบสแต็กเทรซที่บันทึกไว้ ตัวอย่างเช่น:

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

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

เปลี่ยนเป็นอนุญาต

การบังคับใช้ SELinux สามารถปิดใช้งานได้ผ่าน ADB บน ​​userdebug หรือ eng builds ในการทำเช่นนั้น ขั้นแรกให้เปลี่ยน 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 ที่แสดงก่อนหน้านี้จะให้ผลลัพธ์ในคำสั่งนโยบาย SELinux ที่แนะนำต่อไปนี้:

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

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