Viết chính sách SELinux

Dự án nguồn mở Android (AOSP) cung cấp một chính sách cơ sở vững chắc cho các ứng dụng và dịch vụ phổ biến trên tất cả thiết bị Android. Những người đóng góp cho AOSP thường xuyên tinh chỉnh chính sách này. Chính sách cốt lõi dự kiến sẽ chiếm khoảng 90–95% chính sách cuối cùng trên thiết bị, còn 5–10% còn lại là các tuỳ chỉnh dành riêng cho thiết bị. Bài viết này tập trung vào các tuỳ chỉnh dành riêng cho thiết bị, cách viết chính sách dành riêng cho thiết bị và một số sai lầm cần tránh trong quá trình này.

Khởi động thiết bị

Trong khi viết chính sách dành riêng cho thiết bị, hãy làm theo các bước sau.

Chạy ở chế độ cho phép

Khi thiết bị ở chế độ cho phép, các yêu cầu từ chối sẽ được ghi lại nhưng không được thực thi. Chế độ cho phép rất quan trọng vì hai lý do:

  • Chế độ quyền truy cập đảm bảo rằng việc hiển thị chính sách không làm chậm trễ các tác vụ thông báo sớm khác của thiết bị.
  • Lệnh từ chối thực thi có thể che giấu các lệnh từ chối khác. Ví dụ: quyền truy cập vào tệp thường bao gồm việc tìm kiếm thư mục, mở tệp, sau đó đọc tệp. Ở chế độ thực thi, chỉ có trường hợp từ chối tìm kiếm thư mục xảy ra. Chế độ cho phép đảm bảo tất cả các trường hợp từ chối đều được xem.

Cách đơn giản nhất để đưa một thiết bị vào chế độ không bắt buộc là sử dụng dòng lệnh hạt nhân. Bạn có thể thêm tệp này vào tệp BoardConfig.mk của thiết bị: platform/device/<vendor>/<target>/BoardConfig.mk. Sau khi sửa đổi dòng lệnh, hãy thực hiện make clean, sau đó make bootimage và cài đặt ROM hình ảnh khởi động mới.

Sau đó, hãy xác nhận chế độ cho phép bằng:

adb shell getenforce

Hai tuần là khoảng thời gian hợp lý để ở chế độ cho phép toàn cầu. Sau khi giải quyết phần lớn trường hợp từ chối, hãy quay lại thực thi chế độ và giải quyết các lỗi ngay khi chúng xuất hiện. Bạn có thể tạm thời chuyển các miền vẫn đang bị từ chối hoặc các dịch vụ vẫn đang trong quá trình phát triển mạnh mẽ sang chế độ cho phép, nhưng hãy chuyển các miền đó trở lại chế độ thực thi càng sớm càng tốt.

Thực thi sớm

Ở chế độ thực thi, các yêu cầu từ chối đều được ghi lại và thực thi. Bạn nên chuyển thiết bị sang chế độ thực thi càng sớm càng tốt. Việc chờ tạo và thực thi chính sách dành riêng cho thiết bị thường dẫn đến sản phẩm bị lỗi và trải nghiệm người dùng kém. Bắt đầu sớm để tham gia thử nghiệm nội bộ và đảm bảo kiểm thử đầy đủ chức năng trong quá trình sử dụng thực tế. Việc bắt đầu sớm sẽ đảm bảo các mối lo ngại về bảo mật được đưa vào các quyết định thiết kế. Ngược lại, việc cấp quyền chỉ dựa trên các trường hợp từ chối đã quan sát được là một phương pháp không an toàn. Hãy dùng khoảng thời gian này để tiến hành kiểm tra bảo mật của thiết bị và báo cáo lỗi dựa trên hành vi không được phép.

Xoá hoặc xoá chính sách hiện có

Có một số lý do chính đáng để tạo chính sách dành riêng cho thiết bị từ việc tạo chính sách trên thiết bị mới, bao gồm:

Giải quyết các trường hợp từ chối cung cấp dịch vụ chính

Các trường hợp từ chối do các dịch vụ cốt lõi tạo ra thường được giải quyết bằng cách gắn nhãn tệp. Ví dụ:

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

được giải quyết hoàn toàn bằng cách gắn nhãn đúng cách cho /dev/kgsl-3d0. Trong ví dụ này, tcontextdevice. Đây là ngữ cảnh mặc định, trong đó mọi thứ trong /dev đều nhận được nhãn " thiết bị", trừ phi bạn chỉ định một nhãn cụ thể hơn. Việc chỉ chấp nhận kết quả từ audit2allow tại đây sẽ dẫn đến một quy tắc không chính xác và quá dễ dãi.

Để giải quyết loại vấn đề này, hãy gắn nhãn cụ thể hơn cho tệp, trong trường hợp này là gpu_device. Bạn không cần thêm quyền nào vì mediaserver đã có các quyền cần thiết trong chính sách cốt lõi để truy cập vào gpu_device.

Các tệp khác dành riêng cho thiết bị cần được gắn nhãn bằng các loại được xác định trước trong chính sách cốt lõi:

Nói chung, việc cấp quyền cho nhãn mặc định là không đúng. Nhiều quyền trong số này không được phép theo quy tắc neverallow, nhưng ngay cả khi không được cho phép một cách rõ ràng, bạn vẫn nên cung cấp một nhãn cụ thể.

Gắn nhãn các dịch vụ mới và từ chối địa chỉ

Các dịch vụ đã khởi chạy phải chạy trong miền SELinux của riêng chúng. Ví dụ sau đây đặt dịch vụ "foo" vào miền SELinux riêng và cấp cho dịch vụ đó các quyền.

Dịch vụ chạy trong tệp init.device.rc của thiết bị dưới dạng:

service foo /system/bin/foo
    class core
  1. Tạo một miền mới "foo"

    Tạo tệp device/manufacturer/device-name/sepolicy/foo.te có nội dung sau:

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

    Đây là mẫu ban đầu cho miền foo SELinux mà bạn có thể thêm quy tắc dựa trên các thao tác cụ thể do tệp thực thi đó thực hiện.

  2. Nhãn /system/bin/foo

    Thêm nội dung sau vào device/manufacturer/device-name/sepolicy/file_contexts:

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

    Điều này đảm bảo tệp thực thi được gắn nhãn đúng cách để SELinux chạy dịch vụ trong miền thích hợp.

  3. Tạo và cài đặt ROM hình ảnh hệ thống và hình ảnh khởi động.
  4. Tinh chỉnh các quy tắc SELinux cho miền.

    Sử dụng các trường hợp từ chối để xác định các quyền cần thiết. Công cụ audit2allow cung cấp các nguyên tắc hữu ích, nhưng chỉ sử dụng công cụ này để tham khảo khi viết chính sách. Đừng chỉ sao chép kết quả đầu ra.

Chuyển về chế độ thực thi

Bạn có thể khắc phục sự cố ở chế độ không bắt buộc, nhưng hãy chuyển về thực thi chế độ càng sớm càng tốt và cố gắng duy trì ở đó.

Lỗi thường gặp

Sau đây là một số giải pháp cho các lỗi thường gặp khi viết chính sách dành riêng cho thiết bị.

Sử dụng quá nhiều từ phủ định

Quy tắc ví dụ sau đây tương tự như khoá cửa trước nhưng để cửa sổ mở:

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

Mục đích rất rõ ràng: tất cả mọi người trừ các ứng dụng bên thứ ba có thể có quyền truy cập vào thiết bị gỡ lỗi.

Quy tắc này có một vài điểm thiếu sót. Việc loại trừ untrusted_app không hề đơn giản vì có thể tất cả ứng dụng đều có thể chạy các dịch vụ trong miền isolated_app nếu muốn. Tương tự, nếu các miền mới cho ứng dụng bên thứ ba được thêm vào AOSP, thì các miền mới đó cũng có quyền truy cập vào scary_debug_device. Quy tắc quá dễ tính. Hầu hết các miền sẽ không được hưởng lợi khi có quyền truy cập vào công cụ gỡ lỗi này. Bạn nên viết quy tắc để chỉ cho phép những miền cần quyền truy cập.

Gỡ lỗi các tính năng trong phiên bản chính thức

Các tính năng gỡ lỗi không được xuất hiện trên các bản dựng chính thức cũng như không được xuất hiện trong chính sách của các tính năng này.

Giải pháp thay thế đơn giản nhất là chỉ cho phép tính năng gỡ lỗi khi SELinux bị tắt trên các bản dựng eng/userdebug, chẳng hạn như adb rootadb shell setenforce 0.

Một phương án thay thế an toàn khác là đưa quyền gỡ lỗi vào câu lệnh userdebug_or_eng.

Bùng nổ về quy mô chính sách

Bài viết Phân tích các chính sách SEAndroid trong thực tế mô tả một xu hướng đáng lo ngại về sự gia tăng của các tuỳ chỉnh chính sách thiết bị. Chính sách dành riêng cho thiết bị phải chiếm 5–10% chính sách tổng thể chạy trên một thiết bị. Các tuỳ chỉnh trong phạm vi từ 20%trở lên gần như chắc chắn chứa quá nhiều miền đặc quyền và chính sách không còn hiệu lực.

Chính sách quá lớn:

  • Gây ảnh hưởng kép đến bộ nhớ vì chính sách nằm trong ramdisk và cũng được tải vào bộ nhớ nhân.
  • Lãng phí dung lượng ổ đĩa do cần có hình ảnh khởi động lớn hơn.
  • Ảnh hưởng đến thời gian tra cứu chính sách thời gian chạy.

Ví dụ sau đây cho thấy hai thiết bị, trong đó chính sách dành riêng cho nhà sản xuất chiếm 50% và 40% chính sách trên thiết bị. Việc viết lại chính sách này đã mang lại những cải tiến đáng kể về bảo mật mà không làm mất chức năng, như minh hoạ dưới đây. (Các thiết bị AOSP Shamu và Flounder được đưa vào để so sánh.)

Hình 1: So sánh kích thước chính sách dành riêng cho thiết bị sau khi kiểm tra bảo mật.

Hình 1 So sánh kích thước chính sách dành riêng cho thiết bị sau khi kiểm tra bảo mật.

Trong cả hai trường hợp, chính sách đã giảm đáng kể cả về kích thước và số lượng quyền. Kích thước chính sách giảm gần như hoàn toàn là do việc xoá các quyền không cần thiết, nhiều quyền trong số đó có thể là các quy tắc do audit2allow tạo ra và được thêm vào chính sách một cách không phân biệt. Các miền bị hỏng cũng là vấn đề đối với cả hai thiết bị.

Cấp quyền dac_override

Việc từ chối dac_override có nghĩa là quá trình vi phạm đang cố gắng truy cập vào một tệp có các quyền không chính xác của người dùng/nhóm/thế giới trong hệ thống Unix. Giải pháp thích hợp hầu như không bao giờ là cấp quyền dac_override. Thay vào đó, hãy thay đổi quyền unix trên tệp hoặc quy trình. Một số miền như init, voldinstalld thực sự cần có khả năng ghi đè quyền tệp unix để truy cập vào các tệp của các quy trình khác. Hãy xem blog của Dan Walsh để biết thêm thông tin giải thích chi tiết.