mirror of
https://github.com/tiann/KernelSU.git
synced 2025-02-20 11:43:32 +08:00
ksud: refine su and make it compitable with MagiskSU
Supported features: 1. --mount-master, -M, -mm which would make the command run in global mount namespace. 2. - user to switch a specific user's shell. 3. -v, -V to print version code and name. fix #330 #306 #305 #32
This commit is contained in:
parent
6fc8cc7e8e
commit
48e76f9096
10
userspace/ksud/Cargo.lock
generated
10
userspace/ksud/Cargo.lock
generated
@ -584,6 +584,15 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
@ -793,6 +802,7 @@ dependencies = [
|
||||
"encoding",
|
||||
"env_logger",
|
||||
"extattr",
|
||||
"getopts",
|
||||
"humansize",
|
||||
"is_executable",
|
||||
"java-properties",
|
||||
|
@ -32,6 +32,7 @@ rust-embed = { version = "6.4.2", features = [
|
||||
"compression", # must clean build after updating binaries
|
||||
] }
|
||||
which = "4.2.2"
|
||||
getopts = "0.2.21"
|
||||
|
||||
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
|
||||
sys-mount = { git = "https://github.com/tiann/sys-mount" }
|
||||
|
@ -141,7 +141,7 @@ pub fn run() -> Result<()> {
|
||||
// the kernel executes su with argv[0] = "su" and replace it with us
|
||||
let arg0 = std::env::args().next().unwrap_or_default();
|
||||
if arg0 == "su" || arg0 == "/system/bin/su" {
|
||||
return crate::ksu::grant_root();
|
||||
return crate::ksu::root_shell();
|
||||
}
|
||||
|
||||
let cli = Args::parse();
|
||||
|
@ -1,9 +1,19 @@
|
||||
use anyhow::Result;
|
||||
use anyhow::{Ok, Result};
|
||||
|
||||
#[cfg(unix)]
|
||||
use anyhow::ensure;
|
||||
use getopts::Options;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
defs,
|
||||
utils::{self, umask},
|
||||
};
|
||||
|
||||
pub const KERNEL_SU_OPTION: u32 = 0xDEAD_BEEF;
|
||||
|
||||
@ -44,6 +54,180 @@ pub fn grant_root() -> Result<()> {
|
||||
unimplemented!("grant_root is only available on android");
|
||||
}
|
||||
|
||||
fn print_usage(program: &str, opts: Options) {
|
||||
let brief = format!("KernelSU\n\nUsage: {program} [options] [-] [user [argument...]]");
|
||||
print!("{}", opts.usage(&brief));
|
||||
}
|
||||
|
||||
fn set_identity(uid: u32) {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
unsafe {
|
||||
libc::seteuid(uid);
|
||||
libc::setresgid(uid, uid, uid);
|
||||
libc::setresuid(uid, uid, uid);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_shell() -> Result<()> {
|
||||
// we are root now, this was set in kernel!
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let program = args[0].clone();
|
||||
|
||||
let mut opts = Options::new();
|
||||
opts.optopt(
|
||||
"c",
|
||||
"command",
|
||||
"pass COMMAND to the invoked shell",
|
||||
"COMMAND",
|
||||
);
|
||||
opts.optflag("h", "help", "display this help message and exit");
|
||||
opts.optflag("l", "login", "force run in the global mount namespace");
|
||||
opts.optflag(
|
||||
"p",
|
||||
"preserve-environment",
|
||||
"preserve the entire environment",
|
||||
);
|
||||
opts.optflag(
|
||||
"s",
|
||||
"shell",
|
||||
"use SHELL instead of the default /system/bin/sh",
|
||||
);
|
||||
opts.optflag("v", "version", "display version number and exit");
|
||||
opts.optflag("V", "", "display version code and exit");
|
||||
opts.optflag(
|
||||
"M",
|
||||
"mount-master",
|
||||
"force run in the global mount namespace",
|
||||
);
|
||||
|
||||
// Replace -cn with -z, -mm with -M for supporting getopt_long
|
||||
let args = args
|
||||
.into_iter()
|
||||
.map(|e| {
|
||||
if e == "-mm" {
|
||||
"-M".to_string()
|
||||
} else if e == "-cn" {
|
||||
"-z".to_string()
|
||||
} else {
|
||||
e
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let matches = match opts.parse(&args[1..]) {
|
||||
std::result::Result::Ok(m) => m,
|
||||
Err(f) => {
|
||||
println!("{f}");
|
||||
print_usage(&program, opts);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
};
|
||||
|
||||
if matches.opt_present("h") {
|
||||
print_usage(&program, opts);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if matches.opt_present("v") {
|
||||
println!("{}:KernelSU", defs::VERSION_NAME);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if matches.opt_present("V") {
|
||||
println!("{}", defs::VERSION_CODE);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let shell = matches.opt_str("s").unwrap_or("/system/bin/sh".to_string());
|
||||
let mut is_login = matches.opt_present("l");
|
||||
let preserve_env = matches.opt_present("p");
|
||||
let mount_master = matches.opt_present("M");
|
||||
|
||||
let mut free_idx = 0;
|
||||
let command = matches.opt_str("c").map(|cmd| {
|
||||
free_idx = matches.free.len();
|
||||
let mut cmds = vec![];
|
||||
cmds.push(cmd);
|
||||
cmds.extend(matches.free.clone());
|
||||
cmds
|
||||
});
|
||||
|
||||
let mut args = vec![];
|
||||
if let Some(cmd) = command {
|
||||
args.push("-c".to_string());
|
||||
args.push(cmd.join(" "));
|
||||
};
|
||||
|
||||
if free_idx < matches.free.len() && matches.free[free_idx] == "-" {
|
||||
is_login = true;
|
||||
free_idx += 1;
|
||||
}
|
||||
|
||||
let mut uid = 0; // default uid = 0(root)
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if free_idx < matches.free.len() {
|
||||
let name = &matches.free[free_idx];
|
||||
uid = unsafe {
|
||||
match libc::getpwnam(name.as_ptr() as *const u8).as_ref() {
|
||||
Some(pw) => pw.pw_uid,
|
||||
None => name.parse::<u32>().unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/topjohnwu/Magisk/blob/master/native/src/su/su_daemon.cpp#L408
|
||||
let arg0 = if is_login { "-" } else { &shell };
|
||||
|
||||
let mut command = &mut Command::new(&shell);
|
||||
|
||||
if !preserve_env {
|
||||
// This is actually incorrect, i don't know why.
|
||||
// command = command.env_clear();
|
||||
|
||||
let pw = unsafe { libc::getpwuid(uid).as_ref() };
|
||||
|
||||
if let Some(pw) = pw {
|
||||
let home = unsafe { CStr::from_ptr(pw.pw_dir) };
|
||||
let pw_name = unsafe { CStr::from_ptr(pw.pw_name)};
|
||||
|
||||
let home = home.to_string_lossy();
|
||||
let pw_name = pw_name.to_string_lossy();
|
||||
|
||||
command = command
|
||||
.env("HOME", home.as_ref())
|
||||
.env("USER", pw_name.as_ref())
|
||||
.env("LOGNAME", pw_name.as_ref())
|
||||
.env("SHELL", &shell);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
// escape from the current cgroup and become session leader
|
||||
command = command.process_group(0);
|
||||
command = unsafe {
|
||||
command.pre_exec(move || {
|
||||
umask(0o22);
|
||||
utils::switch_cgroups();
|
||||
|
||||
// switch to global mount namespace
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if mount_master {
|
||||
let _ = utils::switch_mnt_ns(1);
|
||||
let _ = utils::unshare_mnt_ns();
|
||||
}
|
||||
|
||||
set_identity(uid);
|
||||
|
||||
std::result::Result::Ok(())
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
command = command.args(args).arg0(arg0);
|
||||
Err(command.exec().into())
|
||||
}
|
||||
|
||||
pub fn get_version() -> i32 {
|
||||
let mut result: i32 = 0;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
|
@ -14,8 +14,8 @@ use log::{info, warn};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env::var as env_var,
|
||||
fs::{remove_dir_all, set_permissions, File, OpenOptions, Permissions},
|
||||
io::{Cursor, Write},
|
||||
fs::{remove_dir_all, set_permissions, File, Permissions},
|
||||
io::Cursor,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
str::FromStr,
|
||||
@ -154,32 +154,6 @@ fn grow_image_size(img: &str, extra_size: u64) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn switch_cgroup(grp: &str, pid: u32) {
|
||||
let path = Path::new(grp).join("cgroup.procs");
|
||||
if !path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let fp = OpenOptions::new().append(true).open(path);
|
||||
if let Ok(mut fp) = fp {
|
||||
let _ = writeln!(fp, "{pid}");
|
||||
}
|
||||
}
|
||||
|
||||
fn switch_cgroups() {
|
||||
let pid = std::process::id();
|
||||
switch_cgroup("/acct", pid);
|
||||
switch_cgroup("/dev/cg2_bpf", pid);
|
||||
switch_cgroup("/sys/fs/cgroup", pid);
|
||||
|
||||
if getprop("ro.config.per_app_memcg")
|
||||
.filter(|prop| prop == "false")
|
||||
.is_none()
|
||||
{
|
||||
switch_cgroup("/dev/memcg/apps", pid);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_sepolicy_rule() -> Result<()> {
|
||||
let modules_dir = Path::new(defs::MODULE_DIR);
|
||||
let dir = std::fs::read_dir(modules_dir)?;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use anyhow::{bail, Context, Error, Ok, Result};
|
||||
use std::{
|
||||
fs::{create_dir_all, write, File},
|
||||
io::ErrorKind::AlreadyExists,
|
||||
fs::{create_dir_all, write, File, OpenOptions},
|
||||
io::{ErrorKind::AlreadyExists, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
@ -116,6 +116,32 @@ pub fn unshare_mnt_ns() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn switch_cgroup(grp: &str, pid: u32) {
|
||||
let path = Path::new(grp).join("cgroup.procs");
|
||||
if !path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let fp = OpenOptions::new().append(true).open(path);
|
||||
if let std::result::Result::Ok(mut fp) = fp {
|
||||
let _ = writeln!(fp, "{pid}");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn switch_cgroups() {
|
||||
let pid = std::process::id();
|
||||
switch_cgroup("/acct", pid);
|
||||
switch_cgroup("/dev/cg2_bpf", pid);
|
||||
switch_cgroup("/sys/fs/cgroup", pid);
|
||||
|
||||
if getprop("ro.config.per_app_memcg")
|
||||
.filter(|prop| prop == "false")
|
||||
.is_none()
|
||||
{
|
||||
switch_cgroup("/dev/memcg/apps", pid);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn umask(mask: u32) {
|
||||
unsafe { libc::umask(mask) };
|
||||
|
Loading…
x
Reference in New Issue
Block a user