เขียนนโยบาย SELinux

โครงการโอเพนซอร์ส Android (AOSP) มอบนโยบายพื้นฐานที่แข็งแกร่งสำหรับแอปและบริการที่ใช้ร่วมกันได้ในอุปกรณ์ Android ทั้งหมด ผู้ร่วมให้ข้อมูลกับ AOSP จะปรับแต่งนโยบายนี้อย่างสม่ำเสมอ นโยบายหลักคาดว่าจะคิดเป็นสัดส่วนประมาณ 90-95% ของนโยบายในอุปกรณ์ขั้นสุดท้าย โดยมีการปรับแต่งเฉพาะอุปกรณ์คิดเป็นสัดส่วน 5-10% ที่เหลือ บทความนี้จะเน้นที่การปรับแต่งเฉพาะอุปกรณ์ วิธีเขียนนโยบายเฉพาะอุปกรณ์ และข้อผิดพลาดบางอย่างที่ควรหลีกเลี่ยง

การเปิดใช้งานอุปกรณ์

ขณะเขียนนโยบายเฉพาะอุปกรณ์ ให้ทำตามขั้นตอนต่อไปนี้

ทำงานในโหมดไม่เข้มงวด

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

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

วิธีที่ง่ายที่สุดในการเปลี่ยนอุปกรณ์เป็นโหมดอนุญาตคือการใช้บรรทัดคำสั่งเคอร์เนล คุณสามารถเพิ่มข้อมูลนี้ลงในไฟล์ BoardConfig.mk ของอุปกรณ์ได้ platform/device/<vendor>/<target>/BoardConfig.mk หลังจากแก้ไขบรรทัดคำสั่งแล้ว ให้กด make clean แล้วกด make bootimage แล้วแฟลชอิมเมจการบูตใหม่

หลังจากนั้น ให้ยืนยันโหมดอนุญาตด้วย

adb shell getenforce

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

บังคับใช้ตั้งแต่เนิ่นๆ

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

นำนโยบายที่มีอยู่ออกหรือลบนโยบาย

มีเหตุผลหลายประการที่ควรสร้างนโยบายเฉพาะอุปกรณ์ตั้งแต่ต้นในอุปกรณ์เครื่องใหม่ ซึ่งรวมถึง

จัดการการปฏิเสธบริการหลัก

โดยปกติแล้ว การปฏิเสธที่เกิดจากบริการหลักจะได้รับการแก้ไขด้วยการติดป้ายกำกับไฟล์ เช่น

avc: denied { open } for pid=1003 comm=”mediaserver” path="/dev/kgsl-3d0”
dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0
tclass=chr_file permissive=1
avc: denied { read write } for pid=1003 name="kgsl-3d0" dev="tmpfs"
scontext=u:r:mediaserver:s0
tcontext=u:object_r:device:s0 tclass=chr_file permissive=1

ได้รับการแก้ไขอย่างสมบูรณ์แล้วด้วยการติดป้ายกํากับ /dev/kgsl-3d0 อย่างถูกต้อง ในตัวอย่างนี้ tcontext คือ device รายการนี้แสดงบริบทเริ่มต้นที่ทุกอย่างใน /dev จะได้รับป้ายกำกับ " อุปกรณ์" เว้นแต่จะมีการกำหนดป้ายกำกับที่เฉพาะเจาะจงมากขึ้น การยอมรับเอาต์พุตจาก audit2allow ที่นี่เพียงอย่างเดียวจะทำให้ได้กฎที่ไม่ถูกต้องและอนุญาตมากเกินไป

หากต้องการแก้ปัญหาประเภทนี้ ให้ติดป้ายกำกับไฟล์ให้เฉพาะเจาะจงมากขึ้น ซึ่งในกรณีนี้คือ gpu_device ไม่จำเป็นต้องมีสิทธิ์เพิ่มเติมเนื่องจาก MediaServer มีสิทธิ์ที่จำเป็นอยู่แล้วในนโยบายหลักในการเข้าถึง GPU_device

ไฟล์อื่นๆ สำหรับอุปกรณ์ที่เฉพาะเจาะจงซึ่งควรติดป้ายกำกับด้วยประเภทที่กำหนดไว้ล่วงหน้าในนโยบายหลัก

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

ติดป้ายกำกับบริการใหม่และแก้ไขการปฏิเสธ

บริการที่เริ่มต้นด้วย Init ต้องทำงานในโดเมน SELinux ของตนเอง ตัวอย่างต่อไปนี้จะใส่บริการ "foo" ไว้ในโดเมน SELinux ของตัวเองและมอบสิทธิ์ให้

บริการนี้มีการเปิดตัวในไฟล์ init.device.rc ของอุปกรณ์ดังนี้

service foo /system/bin/foo
    class core
  1. สร้างโดเมนใหม่ "foo"

    สร้างไฟล์ device/manufacturer/device-name/sepolicy/foo.te ที่มีเนื้อหาต่อไปนี้

    # foo service
    type foo, domain;
    type foo_exec, exec_type, file_type;
    
    init_daemon_domain(foo)
    

    นี่คือเทมเพลตเริ่มต้นสำหรับโดเมน foo SELinux ที่คุณสามารถ เพิ่มกฎตามการดำเนินการเฉพาะที่ดำเนินการโดยไฟล์ปฏิบัติการนั้น

  2. ป้ายกำกับ /system/bin/foo

    เพิ่มข้อมูลต่อไปนี้ลงใน device/manufacturer/device-name/sepolicy/file_contexts

    /system/bin/foo   u:object_r:foo_exec:s0
    

    การดำเนินการนี้จะทำให้ไฟล์ปฏิบัติการมีการติดป้ายกำกับอย่างถูกต้องเพื่อให้ SELinux เรียกใช้บริการในโดเมนที่ถูกต้อง

  3. สร้างและแฟลชอิมเมจการเปิดเครื่องและอิมเมจของระบบ
  4. ปรับแต่งกฎ SELinux สำหรับโดเมน

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

เปลี่ยนกลับไปใช้โหมดบังคับใช้

การแก้ปัญหาในโหมดที่อนุญาตนั้นทำได้ แต่ให้เปลี่ยนไปใช้โหมดบังคับใช้โดยเร็วที่สุดและพยายามใช้โหมดนี้ต่อไป

ความผิดพลาดที่พบบ่อย

ต่อไปนี้เป็นวิธีแก้ปัญหาข้อผิดพลาดที่พบบ่อยเมื่อเขียนนโยบายเฉพาะอุปกรณ์

การใช้การปฏิเสธมากเกินไป

กฎตัวอย่างต่อไปนี้จะเหมือนกับการล็อกประตูหน้าแต่เปิดหน้าต่างทิ้งไว้

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

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

กฎนี้มีข้อบกพร่องอยู่ 2-3 อย่าง การยกเว้น untrusted_app นั้นแก้ไขได้ง่าย เนื่องจากแอปทั้งหมดอาจเลือกที่จะเรียกใช้บริการในโดเมน isolated_app ได้ ในทำนองเดียวกัน หากเพิ่มโดเมนใหม่สำหรับแอปของบุคคลที่สามลงใน AOSP แอปเหล่านั้นจะมีสิทธิ์เข้าถึง scary_debug_device ด้วย กฎอนุญาตมากเกินไป โดเมนส่วนใหญ่จะไม่ได้รับประโยชน์จากการมีสิทธิ์เข้าถึงเครื่องมือแก้ไขข้อบกพร่องนี้ กฎควรเขียนขึ้นเพื่ออนุญาตเฉพาะโดเมนที่ต้องเข้าถึง

แก้ไขข้อบกพร่องของฟีเจอร์ในเวอร์ชันที่ใช้งานจริง

ฟีเจอร์แก้ไขข้อบกพร่องไม่ควรมีอยู่ในบิลด์เวอร์ชันที่ใช้งานจริงและไม่ควรมีนโยบาย

วิธีที่ง่ายที่สุดคือการอนุญาตฟีเจอร์แก้ไขข้อบกพร่องเมื่อปิดใช้ SELinux ในบิลด์ eng/userdebug เช่น adb root และ adb shell setenforce 0 เท่านั้น

อีกทางเลือกที่ปลอดภัยคือการใส่สิทธิ์การแก้ไขข้อบกพร่องในคำสั่ง userdebug_or_eng

นโยบายที่เพิ่มขึ้นอย่างรวดเร็ว

การจำแนกลักษณะของนโยบาย SEAndroid ในสภาพป่าเถื่อน อธิบายแนวโน้มที่น่ากังวลในการเติบโตของการปรับแต่งนโยบายด้านอุปกรณ์ นโยบายเฉพาะอุปกรณ์ควรคิดเป็นสัดส่วน 5-10% ของนโยบายโดยรวมที่ทำงานในอุปกรณ์ การปรับแต่งในช่วง 20%ขึ้นไปมีโดเมนที่มีสิทธิ์มากเกินไปและนโยบายที่ใช้งานไม่ได้เกือบแน่นอน

นโยบายที่มีขนาดใหญ่เกินความจำเป็น

  • เกิดหน่วยความจำแบบ 2 ครั้งเมื่อนโยบายอยู่ใน RAM และโหลดลงในหน่วยความจำของเคอร์เนลด้วย
  • สิ้นเปลืองพื้นที่ในดิสก์เนื่องจากต้องใช้บูตอิมเมจขนาดใหญ่ขึ้น
  • ส่งผลต่อเวลาในการค้นหานโยบายรันไทม์

ตัวอย่างต่อไปนี้แสดงอุปกรณ์ 2 เครื่องที่นโยบายเฉพาะผู้ผลิตคิดเป็นสัดส่วน 50% และ 40% ของนโยบายในอุปกรณ์ การเขียนนโยบายขึ้นใหม่ทำให้มีการปรับปรุงด้านความปลอดภัยอย่างมากโดยไม่สูญเสียฟังก์ชันการทำงาน ดังที่แสดงด้านล่าง (รวมอุปกรณ์ AOSP Shamu และ Flounder ไว้เพื่อการเปรียบเทียบ)

รูปที่ 1: การเปรียบเทียบขนาดนโยบายเฉพาะอุปกรณ์หลังการตรวจสอบความปลอดภัย

รูปที่ 1 การเปรียบเทียบขนาดนโยบายเฉพาะอุปกรณ์หลังจากการตรวจสอบความปลอดภัย

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

ให้สิทธิ์ dac_override

การปฏิเสธ dac_override หมายความว่ากระบวนการที่ละเมิดกำลังพยายามเข้าถึงไฟล์ที่มีสิทธิ์ผู้ใช้/กลุ่ม/โลก Unix ไม่ถูกต้อง โซลูชันที่เหมาะสมคือแทบจะไม่ให้สิทธิ์ dac_override แต่ให้ измените права Unix на файл или процесс โดเมนบางรายการ เช่น init, vold และ installd จำเป็นต้องมีความสามารถในการลบล้างสิทธิ์เข้าถึงไฟล์ Unix เพื่อเข้าถึงไฟล์ของกระบวนการอื่นๆ ดูคำอธิบายโดยละเอียดเพิ่มเติมได้ที่บล็อกของ Dan Walsh