Quy trình init có gần như không giới hạn quyền và sử dụng 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 tạo 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ì 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 tạo thành một phần của giao diện nhị phân ứng dụng hệ thống-nhà cung cấp (ABI) ổn định.
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ăng cường bảo mậ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ế
Vendor init phân nhánh một quy trình con của init sớm trong quá trình khởi động với 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 là một phần của ABI hệ thống-nhà cung cấp ổ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ó, hãy 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 khởi động của nhà cung cấp. Mỗi init tích hợp đều được chú thích bằng một giá trị boolean chỉ định xem lệnh đó có phải được chạy 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 con khởi động của nhà cung cấp và do đó phải tuân theo SEPolicy khởi động 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 được 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 các lệnh này để xử lý quyền không phải SELinux.
Vòng xử lý chính của init chứa một lệnh kiểm tra. 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 liên quy trình (IPC) đến quy trình con khởi động 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 init của nhà cung cấp
Init của nhà cung cấp được bật theo mặc định và các hạn chế của init này áp dụng cho tất cả tập lệnh khởi động có trong phân vùng /vendor. Init của nhà cung cấp sẽ minh bạch đối với các nhà cung cấp có tập lệnh chưa 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ất định của nhà cung cấp vi phạm các hạn chế khởi động của 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. 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ả 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 deniedNếu một lệnh không thành công, bạn có 2 lựa chọn:
- Nếu lệnh không thành công do một hạn chế dự kiến (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 hệ thống-nhà cung cấp ổn định.
- Nếu nhãn SELinux mới và chưa được cấp quyền trong
vendor_init.tehệ 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.tedà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 neverallow có thể bị bỏ qua bằng cách thêm typeattribute 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 độ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 khởi động của nhà cung cấp
hay không.
SEPolicy cho init của nhà cung cấp đượ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.