From a1b5185ecbc41a0a87b7c62df623809b5510ba84 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 14 Mar 2019 22:27:29 -0400 Subject: [PATCH] Make sure rootfs file selabels are correct Android Q init assumes rootfs to always be on EXT4 images, thus never runs restorecon on the whole root directory. This is an issue because some folders in rootfs were set with special selabels in the system partition, but when copying over to initramfs by magiskinit, these labels will not be preserved. So the solution is to relabel the files in rootfs with the original context right? Yes, but rootfs does not allow security xattr to be set on files before the kernel SELinux initializes with genfs_contexts. We have to load our sepolicy to the kernel before we clone the root directory from system partition, which we will also restore the selabel in the meantime. Unfortunately this means that for each reboot, the exact same policy will be loaded to the kernel twice: once in magiskinit so we can label rootfs properly, and once by the original init, which is part of the boot procedure. There is no easy way to prevent init from loading sepolicy, as init will refuse to continue if policy loading has failed. --- native/jni/core/init.cpp | 127 ++++++++++++++++++++---------------- native/jni/include/magisk.h | 10 +-- 2 files changed, 77 insertions(+), 60 deletions(-) diff --git a/native/jni/core/init.cpp b/native/jni/core/init.cpp index 5cc30d6aa..2bb00331f 100644 --- a/native/jni/core/init.cpp +++ b/native/jni/core/init.cpp @@ -112,6 +112,7 @@ static void parse_cmdline(struct cmdline *cmd) { bool skip_initramfs = false, kirin = false, enter_recovery = false; parse_cmdline([&](auto key, auto value) -> void { + LOGD("cmdline: [%s]=[%s]\n", key.data(), value); if (key == "androidboot.slot_suffix") { strcpy(cmd->slot, value); } else if (key == "androidboot.slot") { @@ -130,12 +131,14 @@ static void parse_cmdline(struct cmdline *cmd) { if (kirin && enter_recovery) { // Inform that we are actually booting as recovery - FILE *f = fopen("/.backup/.magisk", "ae"); - fprintf(f, "RECOVERYMODE=true\n"); - fclose(f); + if (FILE *f = fopen("/.backup/.magisk", "ae"); f) { + fprintf(f, "RECOVERYMODE=true\n"); + fclose(f); + } cmd->early_boot = true; } + cmd->early_boot |= skip_initramfs; if (cmd->dt_dir[0] == '\0') @@ -256,18 +259,18 @@ static bool verify_precompiled() { return memcmp(sys_sha, ven_sha, sizeof(sys_sha)) == 0; } -constexpr char SYSTEM_INIT[] = "/system/bin/init"; static bool patch_sepolicy() { - bool init_patch = false; + bool patch_init = false; if (access(SPLIT_PRECOMPILE, R_OK) == 0 && verify_precompiled()) { - init_patch = true; + patch_init = true; load_policydb(SPLIT_PRECOMPILE); } else if (access(SPLIT_PLAT_CIL, R_OK) == 0) { - init_patch = true; + patch_init = true; compile_split_cil(); } else if (access("/sepolicy", R_OK) == 0) { load_policydb("/sepolicy"); } else { + // No selinux in this ROM return false; } @@ -275,37 +278,20 @@ static bool patch_sepolicy() { sepol_allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL); dump_policydb("/sepolicy"); - // Remove the stupid debug sepolicy and use our own + // Load policy to kernel so we can label rootfs + xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr); + dump_policydb(SELINUX_LOAD); + + // Remove OnePlus stupid debug sepolicy and use our own if (access("/sepolicy_debug", F_OK) == 0) { unlink("/sepolicy_debug"); link("/sepolicy", "/sepolicy_debug"); } - if (init_patch) { - // If init is symlink, copy it to rootfs so we can patch - struct stat st; - lstat("/init", &st); - if (S_ISLNK(st.st_mode)) - cp_afc(SYSTEM_INIT, "/init"); + // Enable selinux functions + selinux_builtin_impl(); - char *addr; - size_t size; - mmap_rw("/init", addr, size); - for (char *p = addr; p < addr + size; ++p) { - if (memcmp(p, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL)) == 0) { - // Force init to load /sepolicy - memset(p, 'x', sizeof(SPLIT_PLAT_CIL) - 1); - p += sizeof(SPLIT_PLAT_CIL) - 1; - } else if (memcmp(p, SYSTEM_INIT, sizeof(SYSTEM_INIT)) == 0) { - // Force execute /init instead of /system/bin/init - strcpy(p, "/init"); - p += sizeof(SYSTEM_INIT) - 1; - } - } - munmap(addr, size); - } - - return true; + return patch_init; } static bool unxz(int fd, const uint8_t *buf, size_t size) { @@ -455,16 +441,18 @@ static void setup_overlay() { exit(0); } -static void exec_init(char *argv[]) { +[[noreturn]] static void exec_init(char *argv[]) { // Clean up - umount("/proc"); + umount(SELINUX_MNT); umount("/sys"); + umount("/proc"); if (mnt_system) umount("/system"); if (mnt_vendor) umount("/vendor"); execv("/init", argv); + exit(1); } int main(int argc, char *argv[]) { @@ -506,9 +494,9 @@ int main(int argc, char *argv[]) { full_read("/init", &self, &self_sz); full_read("/.backup/.magisk", &config, &config_sz); - /* *********** + /************* * Initialize - * ***********/ + *************/ int root, sbin; root = open("/", O_RDONLY | O_CLOEXEC); @@ -531,9 +519,9 @@ int main(int argc, char *argv[]) { exec_init(argv); } - /* ************ + /************** * Early Mount - * ************/ + **************/ struct device dev; char partname[32]; @@ -544,15 +532,6 @@ int main(int argc, char *argv[]) { setup_block(&dev, partname); xmkdir("/system_root", 0755); xmount(dev.path, "/system_root", "ext4", MS_RDONLY, nullptr); - int system_root = open("/system_root", O_RDONLY | O_CLOEXEC); - - // Clone rootfs except /system - const char *excl[] = { "system", nullptr }; - excl_list = excl; - clone_dir(system_root, root); - close(system_root); - excl_list = nullptr; - xmkdir("/system", 0755); xmount("/system_root/system", "/system", nullptr, MS_BIND, nullptr); } else if (read_fstab_dt(&cmd, "system", partname, partfs)) { @@ -563,13 +542,51 @@ int main(int argc, char *argv[]) { if (read_fstab_dt(&cmd, "vendor", partname, partfs)) { setup_block(&dev, partname); + xmkdir("/vendor", 0755); xmount(dev.path, "/vendor", partfs, MS_RDONLY, nullptr); mnt_vendor = true; } - /* **************** - * Ramdisk Patches - * ****************/ + /*************** + * Setup Rootfs + ***************/ + + bool patch_init = patch_sepolicy(); + + if (cmd.early_boot) { + // Clone rootfs except /system + int system_root = open("/system_root", O_RDONLY | O_CLOEXEC); + const char *excl[] = { "system", nullptr }; + excl_list = excl; + clone_dir(system_root, root); + close(system_root); + excl_list = nullptr; + } + + if (patch_init) { + constexpr char SYSTEM_INIT[] = "/system/bin/init"; + // If init is symlink, copy it to rootfs so we can patch + struct stat st; + lstat("/init", &st); + if (S_ISLNK(st.st_mode)) + cp_afc(SYSTEM_INIT, "/init"); + + char *addr; + size_t size; + mmap_rw("/init", addr, size); + for (char *p = addr; p < addr + size; ++p) { + if (memcmp(p, SPLIT_PLAT_CIL, sizeof(SPLIT_PLAT_CIL)) == 0) { + // Force init to load /sepolicy + memset(p, 'x', sizeof(SPLIT_PLAT_CIL) - 1); + p += sizeof(SPLIT_PLAT_CIL) - 1; + } else if (memcmp(p, SYSTEM_INIT, sizeof(SYSTEM_INIT)) == 0) { + // Force execute /init instead of /system/bin/init + strcpy(p, "/init"); + p += sizeof(SYSTEM_INIT) - 1; + } + } + munmap(addr, size); + } // Handle ramdisk overlays int fd = open("/overlay", O_RDONLY | O_CLOEXEC); @@ -578,7 +595,11 @@ int main(int argc, char *argv[]) { close(fd); rmdir("/overlay"); } - close(root); + + setup_init_rc(); + + // Don't let init run in init yet + lsetfilecon("/init", "u:object_r:rootfs:s0"); // Create hardlink mirror of /sbin to /root mkdir("/root", 0750); @@ -587,10 +608,6 @@ int main(int argc, char *argv[]) { sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC); link_dir(sbin, root); - setup_init_rc(); - if (patch_sepolicy()) - selinux_builtin_impl(); - // Close all file descriptors for (int i = 0; i < 30; ++i) close(i); diff --git a/native/jni/include/magisk.h b/native/jni/include/magisk.h index 8f7fd6176..e680ae1a8 100644 --- a/native/jni/include/magisk.h +++ b/native/jni/include/magisk.h @@ -31,11 +31,11 @@ #define LEGACYCORE MODULEROOT "/.core" // selinux consts -#define SELINUX_PATH "/sys/fs/selinux" -#define SELINUX_ENFORCE SELINUX_PATH "/enforce" -#define SELINUX_POLICY SELINUX_PATH "/policy" -#define SELINUX_LOAD SELINUX_PATH "/load" -#define SELINUX_CONTEXT SELINUX_PATH "/context" +#define SELINUX_MNT "/sys/fs/selinux" +#define SELINUX_ENFORCE SELINUX_MNT "/enforce" +#define SELINUX_POLICY SELINUX_MNT "/policy" +#define SELINUX_LOAD SELINUX_MNT "/load" +#define SELINUX_CONTEXT SELINUX_MNT "/context" #define SEPOL_PROC_DOMAIN "magisk" #define SEPOL_FILE_DOMAIN "magisk_file"