Khởi tạo nhà cung cấp

Quy trình khởi động có các quyền gần như không bị hạn chế và sử dụng các tập lệnh đầu vào từ cả hệ thống và các phân vùng của nhà cung cấp để khởi động hệ thống trong quá trình khởi động. Quyền truy cập này gây ra một lỗ hổng lớn trong quá trình phân tách hệ thống/nhà cung cấp Treble, vì các tập lệnh của nhà cung cấp có thể hướng dẫn init truy cập vào các tệp, thuộc tính, v.v. không thuộc giao diện nhị phân ứng dụng nhà cung cấp hệ thống ổn định (ABI).

Khởi động nhà cung cấp được thiết kế để khắc phục lỗ hổng này bằng cách sử dụng một miền Linux (SELinux) tăng cường bảo mật riêng biệt vendor_init để chạy các lệnh có trong /vendor với các quyền dành riêng cho nhà cung cấp.

Cơ chế

Quá trình khởi động của nhà cung cấp phân nhánh một quy trình con của quá trình khởi động ngay từ đầu quy trình khởi động bằng bối cảnh SELinux u:r:vendor_init:s0. Ngữ cảnh SELinux này có ít quyền hơn đáng kể so với ngữ cảnh khởi động mặc định và quyền truy cập của ngữ cảnh này chỉ giới hạn ở các tệp, thuộc tính, v.v. dành riêng cho nhà cung cấp hoặc là một phần của ABI nhà cung cấp hệ thống ổn định.

Init kiểm tra từng tập lệnh mà nó tải để xem đường dẫn của tập lệnh đó có bắt đầu bằng /vendor hay không. Nếu có, sẽ gắn thẻ cho tập lệnh đó bằng chỉ báo cho biết các lệnh của tập lệnh phải được chạy trong ngữ cảnh khởi tạo của nhà cung cấp. Mỗi lệnh tích hợp sẵn init được chú thích bằng một boolean chỉ định xem có phải chạy lệnh trong quy trình con khởi động của nhà cung cấp hay không:

  • Hầu hết các lệnh truy cập vào hệ thống tệp đều được chú thích để chạy trong quy trình phụ init của nhà cung cấp, do đó, phải tuân theo init SEPolicy của nhà cung cấp.
  • Hầu hết các lệnh ảnh hưởng đến trạng thái khởi chạy nội bộ (ví dụ: bắt đầu và dừng các dịch vụ) đều chạy trong quy trình khởi chạy thông thường. Các lệnh này được thông báo rằng tập lệnh của nhà cung cấp đang gọi các lệnh này để xử lý các quyền không phải SELinux của riêng chúng.

Vòng lặp xử lý chính của init chứa một quy trình kiểm tra để xác định xem một lệnh có được chú thích để chạy trong quy trình con của nhà cung cấp và bắt nguồn từ tập lệnh của nhà cung cấp hay không. Lệnh đó sẽ được gửi thông qua giao tiếp giữa các quy trình (IPC) đến quy trình con khởi động của nhà cung cấp. Quy trình con này sẽ chạy lệnh và gửi kết quả trở lại init.

Sử dụng tính năng khởi tạo nhà cung cấp

Theo mặc định, tính năng khởi động của nhà cung cấp được bật và các quy tắc hạn chế của tính năng này áp dụng cho tất cả tập lệnh khởi động có trong phân vùng /vendor. Quá trình khởi tạo nhà cung cấp phải minh bạch đối với các nhà cung cấp có tập lệnh không truy cập vào các tệp, thuộc tính, v.v. chỉ dành cho hệ thống.

Tuy nhiên, nếu các lệnh trong một tập lệnh của nhà cung cấp nhất định vi phạm quy định hạn chế trong khởi tạo của nhà cung cấp, thì các lệnh đó sẽ không thực hiện được. Các lệnh không thành công sẽ có một dòng trong nhật ký kernel (hiển thị bằng dmesg) từ init cho biết lỗi. Quá trình kiểm tra SELinux đi kèm với mọi lệnh không thực hiện được do chính sách SELinux. Ví dụ về một lỗi bao gồm cả quy trình kiểm tra SELinux:

type=1400 audit(1511821362.996:9): avc: denied { search } for pid=540 comm="init" name="nfc" dev="sda45" ino=1310721 scontext=u:r:vendor_init:s0 tcontext=u:object_r:nfc_data_file:s0 tclass=dir permissive=0
init: Command 'write /data/nfc/bad_file_access 1234' action=boot (/vendor/etc/init/hw/init.walleye.rc:422) took 2ms and failed: Unable to write to file '/data/nfc/bad_file_access': open() failed: Permission denied

Nếu một lệnh không thành công, bạn có hai lựa chọn:

  • Nếu lệnh không thành công do một hạn chế có chủ ý (chẳng hạn như nếu lệnh đang truy cập vào một tệp hoặc thuộc tính hệ thống), thì lệnh đó phải được triển khai lại theo cách thân thiện với Treble, chỉ thông qua các giao diện ổn định. Các quy tắc Neverallow ngăn việc thêm quyền truy cập vào các tệp hệ thống không thuộc ABI của nhà cung cấp hệ thống ổn định.
  • Nếu nhãn SELinux là mới và chưa được cấp quyền trong vendor_init.te hệ thống cũng như chưa bị loại trừ quyền thông qua các quy tắc neverallow, thì nhãn mới có thể được cấp quyền trong vendor_init.te dành riêng cho thiết bị.

Đối với các thiết bị chạy trước Android 9, bạn có thể bỏ qua các quy tắc neverallows bằng cách thêm thuộc tính loại data_between_core_and_vendor_violators vào tệp vendor_init.te dành riêng cho thiết bị.

Vị trí mã

Phần lớn logic cho IPC khởi tạo của nhà cung cấp nằm trong system/core/init/subcontext.cpp.

Bảng lệnh nằm trong lớp BuiltinFunctionMap trong system/core/init/builtins.cpp và bao gồm các chú thích cho biết liệu lệnh có phải chạy trong quy trình con khởi động của nhà cung cấp hay không.

SEPolicy cho quá trình khởi động của nhà cung cấp được phân chia trên các thư mục riêng tư (system/sepolicy/private/vendor_init.te) và công khai (system/sepolicy/public/vendor_init.te) trong system/sepolicy.