selinux的架构中主要分为:subject和object。subject可以认为是进程,object可以认为是文件(linux中一切皆文件)。在系统代码中,通过撰写sepolicy规则文件(.te),在编译阶段预先为每个关键(subject)进程设置了security context,也为object(文件)设置了security context。那么这些文件是如何生效的呢?
第一步:静态sepolicy文件之间不存在冲突,能正常编译通过;
第二步:编译阶段把sepolicy文件编译成binary文件,并传入到内核;
第三步:内核在启动后以传入的sepolicy文件为原材料,构建起selinux在kernel层的工作框架。
默认第一步已经完成的情况下,重点讨论第二步和第三步的工作流程。
sepolicy文件编译到rom里面后,会保存在rom的某个位置,然后在启动阶段把这个文件传入到kernel中。为了完成这一步需要做以下几个步骤:
1)找到selinuxfs挂载点,一般在/sys/fs/selinux,然后把这个文件写入到该目录下的load文件中;
2)然后导入selinux context文件,加载其中定义的object(文件)的security context;
system/core/init/init.cpp
545int main(int argc, char** argv) {
546 ...
564 bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
565
566 if (is_first_stage) {
567 ...
586 mount("sysfs", "/sys", "sysfs", 0, NULL);
587 mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
588 ...
618 // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
619 global_seccomp();
620
621 // Set up SELinux, loading the SELinux policy.
622 SelinuxSetupKernelLogging();
623 SelinuxInitialize();
624
625 // We're in the kernel domain, so re-exec init to transition to the init domain now
626 // that the SELinux policy has been loaded.
627 if (selinux_android_restorecon("/init", 0) == -1) {
628 PLOG(FATAL) << "restorecon failed of /init failed";
629 }
630
631 setenv("INIT_SECOND_STAGE", "true", 1);
632 ...
644 }
645
646 ...
683 // Now set up SELinux for second stage.
684 SelinuxSetupKernelLogging();
685 SelabelInitialize();
686 SelinuxRestoreContext();
687 ...
其中上述代码中SelinuxInitialize
函数完成把规则文件导入到/sys/fs/selinux/dload
中,流程如下:
SelinuxInitialize=>LoadPolicy=>LoadSplitPolicy=>FindPrecompiledSplitPolicy=>selinux_android_load_policy_from_fd。代码实现如下:
system/core/init/selinux.cpp
201bool FindPrecompiledSplitPolicy(std::string* file) {
202 file->clear();
203 // If there is an odm partition, precompiled_sepolicy will be in
204 // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
205 static constexpr const char vendor_precompiled_sepolicy[] =
206 "/vendor/etc/selinux/precompiled_sepolicy";
207 static constexpr const char odm_precompiled_sepolicy[] =
208 "/odm/etc/selinux/precompiled_sepolicy";
209 if (access(odm_precompiled_sepolicy, R_OK) == 0) {
210 *file = odm_precompiled_sepolicy;
211 } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
212 *file = vendor_precompiled_sepolicy;
213 } else {
214 PLOG(INFO) << "No precompiled sepolicy";
215 return false;
216 }
217 std::string actual_plat_id;
218 if (!ReadFirstLine("/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256", &actual_plat_id)) {
219 PLOG(INFO) << "Failed to read "
220 "/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
221 return false;
222 }
223
224 std::string precompiled_plat_id;
225 std::string precompiled_sha256 = *file + ".plat_and_mapping.sha256";
226 if (!ReadFirstLine(precompiled_sha256.c_str(), &precompiled_plat_id)) {
227 PLOG(INFO) << "Failed to read " << precompiled_sha256;
228 file->clear();
229 return false;
230 }
231 if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
232 file->clear();
233 return false;
234 }
235 return true;
236}
...
252bool IsSplitPolicyDevice() {
253 return access(plat_policy_cil_file, R_OK) != -1;
254}
255
256bool LoadSplitPolicy() {
257 // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
258 // * platform -- policy needed due to logic contained in the system image,
259 // * non-platform -- policy needed due to logic contained in the vendor image,
260 // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
261 // with newer versions of platform policy.
262 //
263 // secilc is invoked to compile the above three policy files into a single monolithic policy
264 // file. This file is then loaded into the kernel.
265
266 // Load precompiled policy from vendor image, if a matching policy is found there. The policy
267 // must match the platform policy on the system image.
268 std::string precompiled_sepolicy_file;
269 if (FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
270 unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
271 if (fd != -1) {
272 if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
273 LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
274 return false;
275 }
276 return true;
277 }
278 }
367}
...
378bool LoadPolicy() {
379 return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
380}
...
384void SelinuxInitialize() {
385 Timer t;
386
387 LOG(INFO) << "Loading SELinux policy";
388 if (!LoadPolicy()) {
389 LOG(FATAL) << "Unable to load SELinux policy";
390 }
391 ...
400 if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result) {
401 LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
402 }
403}
然后通过SelabelInitialize()和SelinuxRestoreContext()把系统中的selinux context文件,加载其中定义的object(文件)的security context。
其中SelabelInitialize的函数中完成了两个步骤:
1)首