1
mirror of https://github.com/topjohnwu/Magisk synced 2024-11-17 01:48:37 +01:00

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 <topjohnwu@gmail.com>
This commit is contained in:
LoveSy 2023-02-12 16:36:38 +08:00 committed by GitHub
parent 03418ddcbf
commit 9e8c68af12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 151 additions and 144 deletions

View File

@ -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<P: AsRef<Path>>(path: P) -> io::Result<io::Lines<io::BufReader<File>>> {
let file = File::open(path)?;
Ok(io::BufReader::new(file).lines())
}

View File

@ -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"

View File

@ -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;

View File

@ -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

View File

@ -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::<i32>().ok().map(|a| {
b.parse::<i32>().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;
}

View File

@ -1,6 +1,7 @@
#include <sys/mount.h>
#include <sys/sysmacros.h>
#include <libgen.h>
#include <inttypes.h>
#include <base.hpp>
#include <selinux.hpp>
@ -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("/");
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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