Xác thực SELinux

Android khuyến khích các OEM kiểm thử kỹ lưỡng việc triển khai SELinux. Khi triển khai SELinux, trước tiên, nhà sản xuất nên áp dụng chính sách mới cho một nhóm thiết bị thử nghiệm.

Sau khi áp dụng một chính sách mới, hãy đảm bảo SELinux đang chạy ở chế độ chính xác trên thiết bị bằng cách đưa ra lệnh getenforce.

Thao tác này sẽ in chế độ SELinux chung: Thực thi hoặc Cho phép. Để xác định chế độ SELinux cho từng miền, bạn phải kiểm tra các tệp tương ứng hoặc chạy phiên bản mới nhất của sepolicy-analyze bằng cờ thích hợp (-p), có trong /platform/system/sepolicy/tools/.

Đọc các trường hợp từ chối

Kiểm tra lỗi, được định tuyến dưới dạng nhật ký sự kiện đến dmesglogcat, đồng thời có thể xem cục bộ trên thiết bị. Các nhà sản xuất nên kiểm tra đầu ra SELinux thành dmesg trên những thiết bị này và tinh chỉnh các chế độ cài đặt trước khi phát hành công khai ở chế độ cho phép và cuối cùng chuyển sang chế độ thực thi. Thông báo nhật ký SELinux chứa avc: nên có thể dễ dàng tìm thấy bằng grep. Bạn có thể ghi lại nhật ký từ chối đang diễn ra bằng cách chạy cat /proc/kmsg hoặc ghi lại nhật ký từ chối từ lần khởi động trước bằng cách chạy cat /sys/fs/pstore/console-ramoops.

Thông báo lỗi SELinux bị giới hạn tốc độ sau khi quá trình khởi động hoàn tất để tránh làm tràn nhật ký. Để đảm bảo bạn thấy tất cả thông báo liên quan, bạn có thể tắt tính năng này bằng cách chạy adb shell auditctl -r 0.

Với đầu ra này, các nhà sản xuất có thể dễ dàng xác định thời điểm người dùng hoặc thành phần hệ thống vi phạm chính sách SELinux. Sau đó, các nhà sản xuất có thể khắc phục hành vi xấu này bằng cách thay đổi phần mềm, chính sách SELinux hoặc cả hai.

Cụ thể, những thông báo nhật ký này cho biết những quy trình sẽ không thành công ở chế độ thực thi và lý do. Dưới đây là ví dụ:

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

Diễn giải kết quả này như sau:

  • Biểu tượng { connectto } ở trên biểu thị hành động đang được thực hiện. Cùng với tclass ở cuối (unix_stream_socket), nó cho bạn biết một cách sơ bộ những gì đã được thực hiện. Trong trường hợp này, có một ứng dụng đang cố gắng kết nối với một socket truyền dữ liệu Unix.
  • scontext (u:r:shell:s0) cho bạn biết ngữ cảnh nào đã bắt đầu hành động. Trong trường hợp này, đây là một thứ gì đó đang chạy dưới dạng shell.
  • tcontext (u:r:netd:s0) cho bạn biết ngữ cảnh của mục tiêu hành động. Trong trường hợp này, đó là unix_stream_socket do netd sở hữu.
  • comm="ping" ở trên cùng cung cấp cho bạn một gợi ý bổ sung về những gì đang chạy tại thời điểm từ chối được tạo. Trong trường hợp này, đó là một gợi ý khá hữu ích.

Ví dụ khác:

adb shell su root dmesg | grep 'avc: '

Kết quả:

<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

Sau đây là những yếu tố chính trong thông báo từ chối này:

  • Hành động – hành động đã thử được đánh dấu trong dấu ngoặc, read write hoặc setenforce.
  • Actor – Mục scontext (ngữ cảnh nguồn) đại diện cho tác nhân, trong trường hợp này là trình nền rmt_storage.
  • Đối tượng – Mục tcontext (ngữ cảnh đích) đại diện cho đối tượng đang được tác động, trong trường hợp này là kmem.
  • Kết quả – Mục tclass (lớp đích) cho biết loại đối tượng đang được xử lý, trong trường hợp này là chr_file (thiết bị ký tự).

Kết xuất ngăn xếp người dùng và ngăn xếp hạt nhân

Trong một số trường hợp, thông tin trong nhật ký sự kiện không đủ để xác định chính xác nguồn gốc của lệnh từ chối. Việc thu thập chuỗi lệnh gọi, bao gồm cả nhân và không gian người dùng, thường rất hữu ích để hiểu rõ hơn lý do xảy ra lỗi từ chối.

Các nhân gần đây xác định một tracepoint có tên là avc:selinux_audited. Sử dụng simpleperf Android để bật điểm theo dõi này và ghi lại chuỗi lệnh gọi.

Cấu hình được hỗ trợ

  • Nhân hệ điều hành Linux >= 5.10, đặc biệt là các nhánh Nhân hệ điều hành chung của Android mainlineandroid12-5.10 được hỗ trợ. Nhánh android12-5.4 cũng được hỗ trợ. Bạn có thể sử dụng simpleperf để xác định xem tracepoint có được xác định trên thiết bị của bạn hay không: adb root && adb shell simpleperf list | grep avc:selinux_audited. Đối với các phiên bản kernel khác, bạn có thể chọn các cam kết dd8166230969bc.
  • Bạn có thể tái tạo sự kiện mà bạn đang gỡ lỗi. Các sự kiện thời gian khởi động không được hỗ trợ bằng simpleperf; tuy nhiên, bạn vẫn có thể khởi động lại dịch vụ để kích hoạt sự kiện.

Ghi lại chuỗi lệnh gọi

Bước đầu tiên là ghi lại sự kiện bằng cách sử dụng simpleperf record:

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

Sau đó, sự kiện gây ra việc từ chối sẽ được kích hoạt. Sau đó, bạn nên dừng ghi. Trong ví dụ này, bằng cách sử dụng Ctrl-c, mẫu phải được ghi lại:

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

Cuối cùng, bạn có thể dùng simpleperf report để kiểm tra stacktrace đã ghi lại. Ví dụ:

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

Chuỗi lệnh gọi ở trên là một chuỗi lệnh gọi hợp nhất của nhân và không gian người dùng. Việc này giúp bạn có cái nhìn rõ ràng hơn về quy trình mã bằng cách bắt đầu dấu vết từ không gian người dùng cho đến tận nhân nơi xảy ra sự từ chối. Để biết thêm thông tin về simpleperf, hãy xem Tài liệu tham khảo về các lệnh có thể thực thi của Simpleperf

Chuyển sang chế độ cho phép

Bạn có thể tắt chế độ thực thi SELinux bằng adb trên các bản dựng userdebug hoặc eng. Để làm như vậy, trước tiên, hãy chuyển ADB sang chế độ root bằng cách chạy adb root. Sau đó, để tắt chế độ thực thi SELinux, hãy chạy:

adb shell setenforce 0

Hoặc tại dòng lệnh của nhân (trong quá trình khởi động thiết bị ban đầu):

androidboot.selinux=permissive
androidboot.selinux=enforcing

Hoặc thông qua bootconfig trong Android 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

Sử dụng audit2allow

Công cụ audit2allow sẽ lấy các lệnh từ chối dmesg và chuyển đổi chúng thành các câu lệnh chính sách SELinux tương ứng. Do đó, công cụ này có thể đẩy nhanh đáng kể quá trình phát triển SELinux.

Để sử dụng, hãy chạy:

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

Tuy nhiên, bạn phải cẩn thận kiểm tra từng trường hợp có thể bổ sung để tránh cấp quyền quá mức. Ví dụ: việc cung cấp audit2allow cho lệnh từ chối rmt_storage đã hiển thị trước đó sẽ dẫn đến câu lệnh chính sách SELinux được đề xuất sau đây:

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

Điều này sẽ cho phép rmt ghi bộ nhớ nhân, một lỗ hổng bảo mật nghiêm trọng. Thông thường, các câu lệnh audit2allow chỉ là điểm bắt đầu. Sau khi sử dụng các câu lệnh này, bạn có thể cần thay đổi miền nguồn và nhãn của mục tiêu, cũng như kết hợp các macro phù hợp để có được một chính sách phù hợp. Đôi khi, việc từ chối đang được xem xét không dẫn đến bất kỳ thay đổi nào về chính sách; thay vào đó, ứng dụng vi phạm cần được thay đổi.