Quy trình khởi động có gần như không hạn chế quyền và sử dụng các tập lệnh đầu vào từ cả phân vùng hệ thống và 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 việc phân chia 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 một phần của giao diện nhị phân ứng dụng ổn định của hệ thống-nhà cung cấp (ABI).
Vendor init được thiết kế để đóng lỗ hổng này bằng cách sử dụng một miền Linux (SELinux) riêng biệt có tính năng bảo mật nâng cao 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ế
Vendor init phân nhánh một quy trình con của init ngay từ đầu trong quy trình khởi động bằng ngữ 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 init 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 thuộc ABI ổn định của nhà cung cấp hệ thống.
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ó, Init sẽ gắn thẻ tập lệnh đó bằng một 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 init của nhà cung cấp. Mỗi init builtin được chú thích bằng một boolean chỉ định xem lệnh có phải chạy trong quy trình con init 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 con init của nhà cung cấp và do đó phải tuân theo SEPolicy init của nhà cung cấp.
- Hầu hết các lệnh ảnh hưởng đến trạng thái khởi động nội bộ (ví dụ: bắt đầu và dừng dịch vụ) đều chạy trong quy trình khởi động thông thường. Các lệnh này được thông báo rằng một tập lệnh của nhà cung cấp đang gọi chúng để xử lý các quyền không phải SELinux của riêng chúng.
Vòng xử lý chính của init chứa một quy trình kiểm tra để đảm bảo rằng nếu một lệnh được chú thích để chạy trong quy trình con của nhà cung cấp và bắt nguồn từ một tập lệnh của nhà cung cấp, thì 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 init của nhà cung cấp. Quy trình này sẽ chạy lệnh và gửi kết quả trở lại init.
Sử dụng vendor init
Theo mặc định, vendor init được bật và các quy tắc hạn chế của vendor init sẽ áp dụng cho tất cả các tập lệnh init có trong phân vùng /vendor
. Quá trình khởi động của nhà cung cấp phải minh bạch đối với những 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 nhà cung cấp cụ thể vi phạm các quy tắc hạn chế khởi tạo nhà cung cấp, thì các lệnh sẽ không thành công. Các lệnh không thành công có một dòng trong nhật ký kernel (hiển thị bằng dmesg) từ init cho biết lỗi. Nhật ký kiểm tra SELinux đi kèm với mọi lệnh không thành công do chính sách SELinux. Ví dụ về lỗi bao gồm cả nhật ký 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 (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 tương thích 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 ổn định của nhà cung cấp hệ thống.
- Nếu nhãn SELinux là nhãn mới và chưa được cấp quyền trong
vendor_init.te
của hệ thống cũng như không 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 trongvendor_init.te
dành riêng cho thiết bị.
Đối với các thiết bị ra mắt trước Android 9, các quy tắc neverallows có thể bị bỏ qua bằng cách thêm thuộc tính data_between_core_and_vendor_violators
vào tệp vendor_init.te
dành riêng cho thiết bị.
Vị trí của mã
Phần lớn logic cho IPC khởi động 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 init của nhà cung cấp hay không.
SEPolicy cho vendor init được chia thành 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.