From 9e8c68af12815625fb47b1f972ddc687a3dd0df5 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sun, 12 Feb 2023 16:36:38 +0800 Subject: [PATCH] Refactor sepolicy.rules resolve We resolve available partitions for sepolicy.rules when patching boot and bind mount the partition by magiskinit. For older devices, the previous logic won't work because the part name is never readable. Co-authored-by: topjohnwu --- native/src/base/files.rs | 9 +++ native/src/include/magisk.hpp | 2 +- native/src/init/init.cpp | 3 + native/src/init/init.hpp | 1 - native/src/init/lib.rs | 74 +++++++++++++++++++++ native/src/init/mount.cpp | 120 ++++++++-------------------------- native/src/init/rootdir.cpp | 13 ++-- native/src/init/selinux.cpp | 35 +++++----- scripts/avd_patch.sh | 2 + scripts/boot_patch.sh | 5 ++ scripts/util_functions.sh | 31 ++------- 11 files changed, 151 insertions(+), 144 deletions(-) diff --git a/native/src/base/files.rs b/native/src/base/files.rs index 75589ceb0..e748b84ba 100644 --- a/native/src/base/files.rs +++ b/native/src/base/files.rs @@ -1,5 +1,9 @@ use std::ffi::CStr; +use std::fs::File; +use std::io; +use std::io::BufRead; use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd}; +use std::path::Path; use libc::{c_char, c_uint, mode_t, EEXIST, ENOENT, O_CLOEXEC, O_PATH}; @@ -131,3 +135,8 @@ pub extern "C" fn mkdirs(path: *const c_char, mode: mode_t) -> i32 { 0 } } + +pub fn read_lines>(path: P) -> io::Result>> { + let file = File::open(path)?; + Ok(io::BufReader::new(file).lines()) +} diff --git a/native/src/include/magisk.hpp b/native/src/include/magisk.hpp index 4d2e230c5..886aadf16 100644 --- a/native/src/include/magisk.hpp +++ b/native/src/include/magisk.hpp @@ -18,7 +18,7 @@ extern std::string MAGISKTMP; #define INTLROOT ".magisk" #define MIRRDIR INTLROOT "/mirror" -#define RULESDIR MIRRDIR "/sepolicy.rules" +#define RULESDIR INTLROOT "/sepolicy.rules" #define BLOCKDIR INTLROOT "/block" #define WORKERDIR INTLROOT "/worker" #define MODULEMNT INTLROOT "/modules" diff --git a/native/src/init/init.cpp b/native/src/init/init.cpp index 8417bd67e..f336dcd92 100644 --- a/native/src/init/init.cpp +++ b/native/src/init/init.cpp @@ -83,6 +83,9 @@ int main(int argc, char *argv[]) { if (name == "magisk"sv) return magisk_proxy_main(argc, argv); + if (name == "magiskinit"sv && argc == 2 && argv[1] == "--rules-device"sv) + return rust::print_rules_device(); + if (getpid() != 1) return 1; diff --git a/native/src/init/init.hpp b/native/src/init/init.hpp index 3d9c95bc0..e75709f1b 100644 --- a/native/src/init/init.hpp +++ b/native/src/init/init.hpp @@ -59,7 +59,6 @@ class MagiskInit : public BaseInit { private: void mount_rules_dir(); protected: - std::string custom_rules_dir; #if ENABLE_AVD_HACK // When this boolean is set, this means we are currently diff --git a/native/src/init/lib.rs b/native/src/init/lib.rs index 17e45b5e9..b53c31f19 100644 --- a/native/src/init/lib.rs +++ b/native/src/init/lib.rs @@ -1,4 +1,9 @@ +use std::env; +use std::path::Path; + pub use base; +use base::libc::{dev_t, makedev}; +use base::read_lines; pub use logging::*; mod logging; @@ -7,5 +12,74 @@ mod logging; pub mod ffi2 { extern "Rust" { fn setup_klog(); + fn print_rules_device() -> i32; } } + +pub fn print_rules_device() -> i32 { + const UNKNOWN: i32 = 0; + const PERSIST: i32 = UNKNOWN + 1; + const METADATA: i32 = PERSIST + 1; + const CACHE: i32 = METADATA + 1; + const UNENCRYPTED: i32 = CACHE + 1; + const DATA: i32 = UNENCRYPTED + 1; + const EXISTING: i32 = DATA + 1; + + let encrypted = env::var("ISENCRYPTED").map_or(false, |var| var == "true"); + + let mut matched = UNKNOWN; + let mut rules_dev: dev_t = 0; + + if let Ok(lines) = read_lines("/proc/self/mountinfo") { + for line in lines { + if let Ok(line) = line { + let new_matched; + if line.contains("/.magisk/sepolicy.rules ") { + new_matched = EXISTING; + } else if line.contains(" - ext4 ") && !line.contains("/dm-") { + if line.contains(" / /cache ") && matched < CACHE { + new_matched = CACHE; + } else if line.contains(" / /data ") && matched < DATA { + if !encrypted { + new_matched = UNENCRYPTED; + } else if Path::new("/data/unencrypted").is_dir() { + new_matched = DATA; + } else { + continue; + } + } else if line.contains(" / /metadata ") && matched < METADATA { + new_matched = METADATA; + } else if (line.contains(" / /persist ") + || line.contains(" / /mnt/vendor/persist ")) + && matched < PERSIST + { + new_matched = PERSIST; + } else { + continue; + } + } else { + continue; + } + if let Some(device) = line.splitn(4, ' ').nth(2) { + device.split_once(':').map(|(a, b)| { + a.parse::().ok().map(|a| { + b.parse::().ok().map(|b| { + rules_dev = unsafe { makedev(a, b) }; + matched = new_matched; + }) + }) + }); + } + } + } + if matched > UNKNOWN { + println!("{rules_dev}"); + return 0; + } else { + eprintln!("Failed to find sepolicy rules partition"); + } + } else { + eprintln!("Error reading /proc/self/mountinfo"); + } + return 1; +} diff --git a/native/src/init/mount.cpp b/native/src/init/mount.cpp index e1ab9424b..60adddce3 100644 --- a/native/src/init/mount.cpp +++ b/native/src/init/mount.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -117,99 +118,32 @@ static void switch_root(const string &path) { } void MagiskInit::mount_rules_dir() { - char path[128]; - xrealpath(BLOCKDIR, blk_info.block_dev, sizeof(blk_info.block_dev)); - xrealpath(MIRRDIR, path, sizeof(path)); - char *b = blk_info.block_dev + strlen(blk_info.block_dev); - char *p = path + strlen(path); - - auto do_mount = [&](const char *type) -> bool { - xmkdir(path, 0755); - bool success = xmount(blk_info.block_dev, path, type, 0, nullptr) == 0; - if (success) - mount_list.emplace_back(path); - return success; - }; - - // First try userdata - strcpy(blk_info.partname, "userdata"); - strcpy(b, "/data"); - strcpy(p, "/data"); - if (setup_block() < 0) { - // Try NVIDIA naming scheme - strcpy(blk_info.partname, "UDA"); - if (setup_block() < 0) - goto cache; - } - // WARNING: DO NOT ATTEMPT TO MOUNT F2FS AS IT MAY CRASH THE KERNEL - // Failure means either f2fs, FDE, or metadata encryption - if (!do_mount("ext4")) - goto cache; - - strcpy(p, "/data/unencrypted"); - if (xaccess(path, F_OK) == 0) { - // FBE, need to use an unencrypted path - custom_rules_dir = path + "/magisk"s; - } else { - // Skip if /data/adb does not exist - strcpy(p, SECURE_DIR); - if (xaccess(path, F_OK) != 0) - return; - strcpy(p, MODULEROOT); - if (xaccess(path, F_OK) != 0) { - goto cache; + dev_t rules_dev = 0; + parse_prop_file(".backup/.magisk", [&rules_dev](auto key, auto value) -> bool { + if (key == "RULESDEVICE") { + sscanf(value.data(), "%" PRIuPTR, &rules_dev); + return false; } - // Unencrypted, directly use module paths - custom_rules_dir = string(path); - } - goto success; - -cache: - // Fallback to cache - strcpy(blk_info.partname, "cache"); - strcpy(b, "/cache"); - strcpy(p, "/cache"); - if (setup_block() < 0) { - // Try NVIDIA naming scheme - strcpy(blk_info.partname, "CAC"); - if (setup_block() < 0) - goto metadata; - } - if (!do_mount("ext4")) - goto metadata; - custom_rules_dir = path + "/magisk"s; - goto success; - -metadata: - // Fallback to metadata - strcpy(blk_info.partname, "metadata"); - strcpy(b, "/metadata"); - strcpy(p, "/metadata"); - if (setup_block() < 0 || !do_mount("ext4")) - goto persist; - custom_rules_dir = path + "/magisk"s; - goto success; - -persist: - // Fallback to persist - strcpy(blk_info.partname, "persist"); - strcpy(b, "/persist"); - strcpy(p, "/persist"); - if (setup_block() < 0 || !do_mount("ext4")) - return; - custom_rules_dir = path + "/magisk"s; - -success: - // Create symlinks so we don't need to go through this logic again - strcpy(p, "/sepolicy.rules"); - if (char *rel = strstr(custom_rules_dir.data(), MIRRDIR)) { - // Create symlink with relative path - char s[128]; - s[0] = '.'; - strscpy(s + 1, rel + sizeof(MIRRDIR) - 1, sizeof(s) - 1); - xsymlink(s, path); - } else { - xsymlink(custom_rules_dir.data(), path); + return true; + }); + if (!rules_dev) return; + xmknod(BLOCKDIR "/rules", S_IFBLK | 0600, rules_dev); + xmkdir(MIRRDIR "/rules", 0); + if (xmount(BLOCKDIR "/rules", MIRRDIR "/rules", "ext4", 0, nullptr) == 0) { + string custom_rules_dir = MIRRDIR "/rules"; + if (access((custom_rules_dir + "/unencrypted").data(), F_OK) == 0) { + custom_rules_dir += "/unencrypted/magisk"; + } else if (access((custom_rules_dir + "/adb").data(), F_OK) == 0) { + custom_rules_dir += "/adb/modules"; + } else { + custom_rules_dir += "/magisk"; + } + // Create bind mount + xmkdirs(RULESDIR, 0); + xmkdirs(custom_rules_dir.data(), 0700); + LOGD("sepolicy.rules: %s -> %s\n", custom_rules_dir.data(), RULESDIR); + xmount(custom_rules_dir.data(), RULESDIR, nullptr, MS_BIND, nullptr); + xumount2(MIRRDIR "/rules", MNT_DETACH); } } @@ -323,7 +257,7 @@ void MagiskInit::setup_tmp(const char *path) { xsymlink("./magisk", applet_names[i]); xsymlink("./magiskpolicy", "supolicy"); - xmount(".", path, nullptr, MS_BIND, nullptr); + xmount(".", path, nullptr, MS_BIND | MS_REC, nullptr); chdir("/"); } diff --git a/native/src/init/rootdir.cpp b/native/src/init/rootdir.cpp index 9cf64b749..4e521c988 100644 --- a/native/src/init/rootdir.cpp +++ b/native/src/init/rootdir.cpp @@ -267,7 +267,8 @@ void RootFSInit::prepare() { rename(backup_init(), "/init"); } -#define PRE_TMPDIR "/magisk-tmp" +#define PRE_TMPSRC "/magisk" +#define PRE_TMPDIR PRE_TMPSRC "/tmp" void MagiskInit::patch_rw_root() { mount_list.emplace_back("/data"); @@ -292,6 +293,8 @@ void MagiskInit::patch_rw_root() { treble = init.contains(SPLIT_PLAT_CIL); } + xmkdir(PRE_TMPSRC, 0); + xmount("tmpfs", PRE_TMPSRC, "tmpfs", 0, "mode=755"); xmkdir(PRE_TMPDIR, 0); setup_tmp(PRE_TMPDIR); chdir(PRE_TMPDIR); @@ -319,10 +322,12 @@ int magisk_proxy_main(int argc, char *argv[]) { unlink("/sbin/magisk"); // Move tmpfs to /sbin - // For some reason MS_MOVE won't work, as a workaround bind mount then unmount - xmount(PRE_TMPDIR, "/sbin", nullptr, MS_BIND | MS_REC, nullptr); - xumount2(PRE_TMPDIR, MNT_DETACH); + // make parent private before MS_MOVE + xmount(nullptr, PRE_TMPSRC, nullptr, MS_PRIVATE, nullptr); + xmount(PRE_TMPDIR, "/sbin", nullptr, MS_MOVE, nullptr); + xumount2(PRE_TMPSRC, MNT_DETACH); rmdir(PRE_TMPDIR); + rmdir(PRE_TMPSRC); // Create symlinks pointing back to /root recreate_sbin("/root", false); diff --git a/native/src/init/selinux.cpp b/native/src/init/selinux.cpp index 350d02e48..eceab94ba 100644 --- a/native/src/init/selinux.cpp +++ b/native/src/init/selinux.cpp @@ -15,14 +15,14 @@ void MagiskInit::patch_sepolicy(const char *in, const char *out) { sepol->magisk_rules(); // Custom rules - if (!custom_rules_dir.empty()) { - if (auto dir = xopen_dir(custom_rules_dir.data())) { - for (dirent *entry; (entry = xreaddir(dir.get()));) { - auto rule = custom_rules_dir + "/" + entry->d_name + "/sepolicy.rule"; - if (xaccess(rule.data(), R_OK) == 0) { - LOGD("Loading custom sepolicy patch: [%s]\n", rule.data()); - sepol->load_rule_file(rule.data()); - } + if (auto dir = xopen_dir(RULESDIR)) { + for (dirent *entry; (entry = xreaddir(dir.get()));) { + auto rule = RULESDIR "/"s + entry->d_name + "/sepolicy.rule"; + if (xaccess(rule.data(), R_OK) == 0 && + access((RULESDIR "/"s + entry->d_name + "/disable").data(), F_OK) != 0 && + access((RULESDIR "/"s + entry->d_name + "/remove").data(), F_OK) != 0) { + LOGD("Loading custom sepolicy patch: [%s]\n", rule.data()); + sepol->load_rule_file(rule.data()); } } } @@ -96,19 +96,18 @@ bool MagiskInit::hijack_sepolicy() { // Read all custom rules into memory string rules; - if (!custom_rules_dir.empty()) { - if (auto dir = xopen_dir(custom_rules_dir.data())) { - for (dirent *entry; (entry = xreaddir(dir.get()));) { - auto rule_file = custom_rules_dir + "/" + entry->d_name + "/sepolicy.rule"; - if (xaccess(rule_file.data(), R_OK) == 0) { - LOGD("Load custom sepolicy patch: [%s]\n", rule_file.data()); - full_read(rule_file.data(), rules); - rules += '\n'; - } + if (auto dir = xopen_dir(RULESDIR)) { + for (dirent *entry; (entry = xreaddir(dir.get()));) { + auto rule_file = RULESDIR "/"s + entry->d_name + "/sepolicy.rule"; + if (xaccess(rule_file.data(), R_OK) == 0 && + access((RULESDIR "/"s + entry->d_name + "/disable").data(), F_OK) != 0 && + access((RULESDIR "/"s + entry->d_name + "/remove").data(), F_OK) != 0) { + LOGD("Load custom sepolicy patch: [%s]\n", rule_file.data()); + full_read(rule_file.data(), rules); + rules += '\n'; } } } - // Create a new process waiting for init operations if (xfork()) { // In parent, return and continue boot process diff --git a/scripts/avd_patch.sh b/scripts/avd_patch.sh index 66728245d..ca6e68164 100644 --- a/scripts/avd_patch.sh +++ b/scripts/avd_patch.sh @@ -59,6 +59,8 @@ cp ramdisk.cpio ramdisk.cpio.orig touch config +echo "RULESDEVICE=$(ISENCRYPTED=true ./magiskinit --rules-device)" >> config + # For API 28, we also patch advancedFeatures.ini to disable SAR # Manually override skip_initramfs by setting RECOVERYMODE=true [ $API = "28" ] && echo 'RECOVERYMODE=true' >> config diff --git a/scripts/boot_patch.sh b/scripts/boot_patch.sh index 068097c38..07e286f13 100644 --- a/scripts/boot_patch.sh +++ b/scripts/boot_patch.sh @@ -73,12 +73,16 @@ fi [ -z $KEEPFORCEENCRYPT ] && KEEPFORCEENCRYPT=false [ -z $PATCHVBMETAFLAG ] && PATCHVBMETAFLAG=false [ -z $RECOVERYMODE ] && RECOVERYMODE=false +[ -z $ISENCRYPTED ] && ISENCRYPTED=false export KEEPVERITY export KEEPFORCEENCRYPT export PATCHVBMETAFLAG +export ISENCRYPTED chmod -R 755 . +RULESDEVICE="$(./magiskinit --rules-device)" || abort "! Unable to find rules partition!" + ######### # Unpack ######### @@ -152,6 +156,7 @@ echo "KEEPVERITY=$KEEPVERITY" > config echo "KEEPFORCEENCRYPT=$KEEPFORCEENCRYPT" >> config echo "PATCHVBMETAFLAG=$PATCHVBMETAFLAG" >> config echo "RECOVERYMODE=$RECOVERYMODE" >> config +echo "RULESDEVICE=$RULESDEVICE" >> config [ ! -z $SHA1 ] && echo "SHA1=$SHA1" >> config # Compress to save precious ramdisk space diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index c8ef7de5c..9f160fced 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -637,37 +637,14 @@ run_migrations() { } copy_sepolicy_rules() { - # Remove all existing rule folders - rm -rf /data/unencrypted/magisk /cache/magisk /metadata/magisk /persist/magisk /mnt/vendor/persist/magisk - - # Find current active RULESDIR - local RULESDIR - local ACTIVEDIR=$(magisk --path)/.magisk/mirror/sepolicy.rules - if [ -L $ACTIVEDIR ]; then - RULESDIR=$(readlink $ACTIVEDIR) - [ "${RULESDIR:0:1}" != "/" ] && RULESDIR="$(magisk --path)/.magisk/mirror/$RULESDIR" - elif ! $ISENCRYPTED; then - RULESDIR=$NVBASE/modules - elif [ -d /data/unencrypted ] && ! grep ' /data ' /proc/mounts | grep -qE 'dm-|f2fs'; then - RULESDIR=/data/unencrypted/magisk - elif grep ' /cache ' /proc/mounts | grep -q 'ext4' ; then - RULESDIR=/cache/magisk - elif grep ' /metadata ' /proc/mounts | grep -q 'ext4' ; then - RULESDIR=/metadata/magisk - elif grep ' /persist ' /proc/mounts | grep -q 'ext4' ; then - RULESDIR=/persist/magisk - elif grep ' /mnt/vendor/persist ' /proc/mounts | grep -q 'ext4' ; then - RULESDIR=/mnt/vendor/persist/magisk - else + local RULESDIR=$(magisk --path)/.magisk/sepolicy.rules + if ! grep -q " $RULESDIR " /proc/mounts; then ui_print "- Unable to find sepolicy rules dir" return 1 fi - if [ -d ${RULESDIR%/magisk} ]; then - echo "RULESDIR=$RULESDIR" >&2 - else - ui_print "- Unable to find sepolicy rules dir ${RULESDIR%/magisk}" - return 1 + if ! grep -q "/adb/modules $RULESDIR " /proc/self/mountinfo; then + rm -rf $RULESDIR/* fi # Copy all enabled sepolicy.rule