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