Migrate connect.cpp to Rust

This commit is contained in:
topjohnwu
2025-08-08 17:27:45 -07:00
committed by John Wu
parent 6839cb9ab2
commit cd0eca20b0
9 changed files with 302 additions and 254 deletions

View File

@@ -24,7 +24,6 @@ LOCAL_SRC_FILES := \
core/core-rs.cpp \ core/core-rs.cpp \
core/resetprop/resetprop.cpp \ core/resetprop/resetprop.cpp \
core/su/su.cpp \ core/su/su.cpp \
core/su/connect.cpp \
core/zygisk/entry.cpp \ core/zygisk/entry.cpp \
core/zygisk/module.cpp \ core/zygisk/module.cpp \
core/zygisk/hook.cpp \ core/zygisk/hook.cpp \

View File

@@ -140,8 +140,14 @@ pub struct FileAttr {
pub con: crate::Utf8CStrBufArr<128>, pub con: crate::Utf8CStrBufArr<128>,
} }
impl Default for FileAttr {
fn default() -> Self {
Self::new()
}
}
impl FileAttr { impl FileAttr {
fn new() -> Self { pub fn new() -> Self {
FileAttr { FileAttr {
st: unsafe { mem::zeroed() }, st: unsafe { mem::zeroed() },
#[cfg(feature = "selinux")] #[cfg(feature = "selinux")]

View File

@@ -112,9 +112,6 @@ void update_deny_flags(int uid, rust::Str process, uint32_t &flags);
// MagiskSU // MagiskSU
void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode); void exec_root_shell(int client, int pid, SuRequest &req, MntNsMode mode);
void app_log(const SuAppRequest &info, SuPolicy policy, bool notify);
void app_notify(const SuAppRequest &info, SuPolicy policy);
int app_request(const SuAppRequest &info);
// Rust bindings // Rust bindings
static inline rust::Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); } static inline rust::Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); }

View File

@@ -161,9 +161,6 @@ pub mod ffi {
fn set_zygisk_prop(); fn set_zygisk_prop();
fn restore_zygisk_prop(); fn restore_zygisk_prop();
fn switch_mnt_ns(pid: i32) -> i32; fn switch_mnt_ns(pid: i32) -> i32;
fn app_request(req: &SuAppRequest) -> i32;
fn app_notify(req: &SuAppRequest, policy: SuPolicy);
fn app_log(req: &SuAppRequest, policy: SuPolicy, notify: bool);
fn exec_root_shell(client: i32, pid: i32, req: &mut SuRequest, mode: MntNsMode); fn exec_root_shell(client: i32, pid: i32, req: &mut SuRequest, mode: MntNsMode);
include!("include/sqlite.hpp"); include!("include/sqlite.hpp");
@@ -211,7 +208,7 @@ pub mod ffi {
fn send_fds(socket: i32, fds: &[i32]) -> bool; fn send_fds(socket: i32, fds: &[i32]) -> bool;
fn recv_fd(socket: i32) -> i32; fn recv_fd(socket: i32) -> i32;
fn recv_fds(socket: i32) -> Vec<i32>; fn recv_fds(socket: i32) -> Vec<i32>;
unsafe fn write_to_fd(self: &SuRequest, fd: i32); fn write_to_fd(self: &SuRequest, fd: i32);
fn pump_tty(infd: i32, outfd: i32); fn pump_tty(infd: i32, outfd: i32);
fn get_pty_num(fd: i32) -> i32; fn get_pty_num(fd: i32) -> i32;
fn restore_stdin() -> bool; fn restore_stdin() -> bool;
@@ -266,7 +263,7 @@ unsafe impl ExternType for UCred {
} }
impl SuRequest { impl SuRequest {
unsafe fn write_to_fd(&self, fd: i32) { fn write_to_fd(&self, fd: i32) {
unsafe { unsafe {
let mut w = ManuallyDrop::new(File::from_raw_fd(fd)); let mut w = ManuallyDrop::new(File::from_raw_fd(fd));
self.encode(w.deref_mut()).ok(); self.encode(w.deref_mut()).ok();

View File

@@ -1,232 +0,0 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <base.hpp>
#include <consts.hpp>
#include <core.hpp>
using namespace std;
#define CALL_PROVIDER \
"/system/bin/app_process", "/system/bin", "com.android.commands.content.Content", \
"call", "--uri", target, "--user", user, "--method", action
#define START_ACTIVITY \
"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \
"start", "-p", target, "--user", user, "-a", "android.intent.action.VIEW", \
"-f", "0x18800020", "--es", "action", action
// 0x18800020 = FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|
// FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS|FLAG_INCLUDE_STOPPED_PACKAGES
class Extra {
const char *key;
enum {
INT,
BOOL,
STRING,
INTLIST,
} type;
union {
int int_val;
bool bool_val;
const char *str_val;
const vector<uint32_t> *intlist_val;
};
string str;
public:
Extra(const char *k, int v): key(k), type(INT), int_val(v) {}
Extra(const char *k, bool v): key(k), type(BOOL), bool_val(v) {}
Extra(const char *k, const char *v): key(k), type(STRING), str_val(v) {}
Extra(const char *k, const vector<uint32_t> *v): key(k), type(INTLIST), intlist_val(v) {}
void add_intent(vector<const char *> &vec) {
const char *val;
switch (type) {
case INT:
vec.push_back("--ei");
str = to_string(int_val);
val = str.data();
break;
case BOOL:
vec.push_back("--ez");
val = bool_val ? "true" : "false";
break;
case STRING:
vec.push_back("--es");
val = str_val;
break;
case INTLIST:
vec.push_back("--es");
for (auto i : *intlist_val) {
str += to_string(i);
str += ",";
}
if (!str.empty()) str.pop_back();
val = str.data();
break;
}
vec.push_back(key);
vec.push_back(val);
}
void add_bind(vector<const char *> &vec) {
char buf[32];
str = key;
switch (type) {
case INT:
str += ":i:";
ssprintf(buf, sizeof(buf), "%d", int_val);
str += buf;
break;
case BOOL:
str += ":b:";
str += bool_val ? "true" : "false";
break;
case STRING:
str += ":s:";
if (SDK_INT >= 30) {
string tmp = str_val;
replace_all(tmp, "\\", "\\\\");
replace_all(tmp, ":", "\\:");
str += tmp;
} else {
str += str_val;
}
break;
case INTLIST:
str += ":s:";
for (auto i : *intlist_val) {
str += to_string(i);
str += ",";
}
if (str.back() == ',') str.pop_back();
break;
}
vec.push_back("--extra");
vec.push_back(str.data());
}
};
static bool check_no_error(int fd) {
char buf[1024];
auto out = xopen_file(fd, "r");
while (fgets(buf, sizeof(buf), out.get())) {
if (strncasecmp(buf, "Error", 5) == 0) {
LOGD("exec_cmd: %s\n", buf);
return false;
}
}
return true;
}
static void exec_cmd(const char *action, vector<Extra> &data,
const SuAppRequest &info, bool provider = true) {
char target[128];
char user[4];
ssprintf(user, sizeof(user), "%d", to_user_id(info.eval_uid));
// First try content provider call method
if (provider) {
ssprintf(target, sizeof(target), "content://%.*s.provider",
(int) info.mgr_pkg.size(), info.mgr_pkg.data());
vector<const char *> args{ CALL_PROVIDER };
for (auto &e : data) {
e.add_bind(args);
}
args.push_back(nullptr);
exec_t exec {
.err = true,
.fd = -1,
.pre_exec = [] { setenv("CLASSPATH", "/system/framework/content.jar", 1); },
.argv = args.data()
};
exec_command_sync(exec);
if (check_no_error(exec.fd))
return;
}
// Then try start activity with package name
ssprintf(target, sizeof(target), "%.*s", (int) info.mgr_pkg.size(), info.mgr_pkg.data());
vector<const char *> args{ START_ACTIVITY };
for (auto &e : data) {
e.add_intent(args);
}
args.push_back(nullptr);
exec_t exec {
.fd = -2,
.pre_exec = [] { setenv("CLASSPATH", "/system/framework/am.jar", 1); },
.fork = fork_dont_care,
.argv = args.data()
};
exec_command(exec);
}
void app_log(const SuAppRequest &info, SuPolicy policy, bool notify) {
if (fork_dont_care() == 0) {
string context = (string) info.request.context;
string command = info.request.command.empty()
? (string) info.request.shell
: (string) info.request.command;
vector<Extra> extras;
extras.reserve(9);
extras.emplace_back("from.uid", info.uid);
extras.emplace_back("to.uid", info.request.target_uid);
extras.emplace_back("pid", info.pid);
extras.emplace_back("policy", +policy);
extras.emplace_back("target", info.request.target_pid);
extras.emplace_back("context", context.data());
extras.emplace_back("gids", &info.request.gids);
extras.emplace_back("command", command.data());
extras.emplace_back("notify", notify);
exec_cmd("log", extras, info);
exit(0);
}
}
void app_notify(const SuAppRequest &info, SuPolicy policy) {
if (fork_dont_care() == 0) {
vector<Extra> extras;
extras.reserve(3);
extras.emplace_back("from.uid", info.uid);
extras.emplace_back("pid", info.pid);
extras.emplace_back("policy", +policy);
exec_cmd("notify", extras, info);
exit(0);
}
}
int app_request(const SuAppRequest &info) {
// Create FIFO
char fifo[64];
ssprintf(fifo, sizeof(fifo), "%s/" INTLROOT "/su_request_%d", get_magisk_tmp(), info.pid);
mkfifo(fifo, 0600);
chown(fifo, info.mgr_uid, info.mgr_uid);
setfilecon(fifo, MAGISK_FILE_CON);
// Send request
vector<Extra> extras;
extras.reserve(3);
extras.emplace_back("fifo", fifo);
extras.emplace_back("uid", info.eval_uid);
extras.emplace_back("pid", info.pid);
exec_cmd("request", extras, info, false);
// Wait for data input for at most 70 seconds
// Open with O_RDWR to prevent FIFO open block
int fd = xopen(fifo, O_RDWR | O_CLOEXEC);
struct pollfd pfd = {
.fd = fd,
.events = POLLIN
};
if (xpoll(&pfd, 1, 70 * 1000) <= 0) {
close(fd);
fd = -1;
}
unlink(fifo);
return fd;
}

View File

@@ -0,0 +1,284 @@
use crate::consts::{INTERNAL_DIR, MAGISK_FILE_CON};
use crate::daemon::{MagiskD, to_user_id};
use crate::ffi::{SuAppRequest, SuPolicy, get_magisk_tmp};
use ExtraVal::{Bool, Int, IntList, Str};
use base::{
BytesExt, FileAttr, LibcReturn, LoggedResult, OsError, ResultExt, cstr, fork_dont_care, info,
libc,
};
use libc::pollfd as PollFd;
use num_traits::AsPrimitive;
use std::{fmt::Write, fs::File, os::fd::AsRawFd, process::Command, process::exit};
struct Extra {
key: &'static str,
value: ExtraVal,
}
enum ExtraVal {
Int(i32),
Bool(bool),
Str(String),
IntList(Vec<u32>),
}
impl Extra {
fn add_intent(&self, cmd: &mut Command) {
match &self.value {
Int(i) => {
cmd.args(["--ei", self.key, &i.to_string()]);
}
Bool(b) => {
cmd.args(["--ez", self.key, &b.to_string()]);
}
Str(s) => {
cmd.args(["--es", self.key, s]);
}
IntList(list) => {
cmd.args(["--es", self.key]);
let mut tmp = String::new();
list.iter().for_each(|i| write!(&mut tmp, "{i},").unwrap());
tmp.pop();
cmd.arg(&tmp);
}
}
}
fn add_bind(&self, cmd: &mut Command) {
let mut tmp: String;
match &self.value {
Int(i) => {
tmp = format!("{}:i:{}", self.key, i);
}
Bool(b) => {
tmp = format!("{}:b:{}", self.key, b);
}
Str(s) => {
let s = s.replace("\\", "\\\\").replace(":", "\\:");
tmp = format!("{}:s:{}", self.key, s);
}
IntList(list) => {
tmp = format!("{}:s:", self.key);
if !list.is_empty() {
list.iter().for_each(|i| write!(&mut tmp, "{i},").unwrap());
tmp.pop();
}
}
}
cmd.args(["--extra", &tmp]);
}
fn add_bind_legacy(&self, cmd: &mut Command) {
match &self.value {
Str(s) => {
let tmp = format!("{}:s:{}", self.key, s);
cmd.args(["--extra", &tmp]);
}
_ => self.add_bind(cmd),
}
}
}
impl MagiskD {
fn exec_cmd(
&self,
action: &'static str,
extras: &[Extra],
info: &SuAppRequest,
use_provider: bool,
) {
let user = to_user_id(info.eval_uid);
let user = user.to_string();
if use_provider {
let provider = format!("content://{}.provider", info.mgr_pkg);
let mut cmd = Command::new("/system/bin/app_process");
cmd.args([
"/system/bin",
"com.android.commands.content.Content",
"call",
"--uri",
&provider,
"--user",
&user,
"--method",
action,
]);
if self.sdk_int() >= 30 {
extras.iter().for_each(|e| e.add_bind(&mut cmd))
} else {
extras.iter().for_each(|e| e.add_bind_legacy(&mut cmd))
}
cmd.env("CLASSPATH", "/system/framework/content.jar");
if let Ok(output) = cmd.output()
&& !output.stderr.contains(b"Error")
&& !output.stdout.contains(b"Error")
{
// The provider call succeed
return;
}
}
let mut cmd = Command::new("/system/bin/app_process");
cmd.args([
"/system/bin",
"com.android.commands.am.Am",
"start",
"-p",
info.mgr_pkg,
"--user",
&user,
"-a",
"android.intent.action.VIEW",
"-f",
// FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|
// FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS|FLAG_INCLUDE_STOPPED_PACKAGES
"0x18800020",
"--es",
"action",
action,
]);
extras.iter().for_each(|e| e.add_intent(&mut cmd));
cmd.env("CLASSPATH", "/system/framework/am.jar");
// Sometimes `am start` will fail, keep trying until it works
loop {
if let Ok(output) = cmd.output()
&& !output.stdout.is_empty()
{
break;
}
}
}
pub fn app_request(&self, info: &SuAppRequest) -> LoggedResult<File> {
let mut fifo = cstr::buf::new::<64>();
fifo.write_fmt(format_args!(
"{}/{}/su_request_{}",
get_magisk_tmp(),
INTERNAL_DIR,
info.pid
))
.ok();
let fd: LoggedResult<File> = try {
let mut attr = FileAttr::new();
attr.st.st_mode = 0o600;
attr.st.st_uid = info.mgr_uid.as_();
attr.st.st_gid = info.mgr_uid.as_();
attr.con.write_str(MAGISK_FILE_CON).ok();
fifo.mkfifo(0o600)?;
fifo.set_attr(&attr)?;
let extras = [
Extra {
key: "fifo",
value: Str(fifo.to_string()),
},
Extra {
key: "uid",
value: Int(info.eval_uid),
},
Extra {
key: "pid",
value: Int(info.pid),
},
];
self.exec_cmd("request", &extras, info, false);
// Open with O_RDWR to prevent FIFO open block
let fd = fifo.open(libc::O_RDWR | libc::O_CLOEXEC)?;
// Wait for data input for at most 70 seconds
let mut pfd = PollFd {
fd: fd.as_raw_fd(),
events: libc::POLLIN,
revents: 0,
};
if unsafe { libc::poll(&mut pfd, 1, 70 * 1000).as_os_result("poll", None, None)? } == 0
{
Err(OsError::with_os_error(libc::ETIMEDOUT, "poll", None, None))?;
}
fd
};
fifo.remove().log_ok();
fd
}
pub fn app_notify(&self, info: &SuAppRequest, policy: SuPolicy) {
if fork_dont_care() != 0 {
return;
}
let extras = [
Extra {
key: "from.uid",
value: Int(info.uid),
},
Extra {
key: "pid",
value: Int(info.pid),
},
Extra {
key: "policy",
value: Int(policy.repr),
},
];
self.exec_cmd("notify", &extras, info, true);
exit(0);
}
pub fn app_log(&self, info: &SuAppRequest, policy: SuPolicy, notify: bool) {
if fork_dont_care() != 0 {
return;
}
let command = if info.request.command.is_empty() {
&info.request.shell
} else {
&info.request.command
};
let extras = [
Extra {
key: "from.uid",
value: Int(info.uid),
},
Extra {
key: "to.uid",
value: Int(info.request.target_uid),
},
Extra {
key: "pid",
value: Int(info.pid),
},
Extra {
key: "policy",
value: Int(policy.repr),
},
Extra {
key: "target",
value: Int(info.request.target_pid),
},
Extra {
key: "context",
value: Str(info.request.context.clone()),
},
Extra {
key: "gids",
value: IntList(info.request.gids.clone()),
},
Extra {
key: "command",
value: Str(command.clone()),
},
Extra {
key: "notify",
value: Bool(notify),
},
];
self.exec_cmd("log", &extras, info, true);
exit(0);
}
}

View File

@@ -1,13 +1,10 @@
use crate::UCred; use crate::UCred;
use crate::daemon::{AID_ROOT, AID_SHELL, MagiskD, to_app_id, to_user_id}; use crate::daemon::{AID_ROOT, AID_SHELL, MagiskD, to_app_id, to_user_id};
use crate::db::{DbSettings, MultiuserMode, RootAccess}; use crate::db::{DbSettings, MultiuserMode, RootAccess};
use crate::ffi::{ use crate::ffi::{SuAppRequest, SuPolicy, SuRequest, exec_root_shell};
SuAppRequest, SuPolicy, SuRequest, app_log, app_notify, app_request, exec_root_shell,
};
use crate::socket::IpcRead; use crate::socket::IpcRead;
use crate::su::db::RootSettings; use crate::su::db::RootSettings;
use base::{LoggedResult, ResultExt, WriteExt, debug, error, exit_on_error, libc, warn}; use base::{LoggedResult, ResultExt, WriteExt, debug, error, exit_on_error, libc, warn};
use std::fs::File;
use std::os::fd::{FromRawFd, IntoRawFd}; use std::os::fd::{FromRawFd, IntoRawFd};
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@@ -145,11 +142,7 @@ impl MagiskD {
let mut access = info.access.lock().unwrap(); let mut access = info.access.lock().unwrap();
if access.settings.policy == SuPolicy::Query { if access.settings.policy == SuPolicy::Query {
let fd = app_request(&app_req); if let Ok(mut fd) = self.app_request(&app_req) {
if fd < 0 {
access.settings.policy = SuPolicy::Deny;
} else {
let mut fd = unsafe { File::from_raw_fd(fd) };
access.settings.policy = SuPolicy { access.settings.policy = SuPolicy {
repr: fd repr: fd
.read_decodable::<i32>() .read_decodable::<i32>()
@@ -157,13 +150,15 @@ impl MagiskD {
.map(i32::from_be) .map(i32::from_be)
.unwrap_or(SuPolicy::Deny.repr), .unwrap_or(SuPolicy::Deny.repr),
}; };
} else {
access.settings.policy = SuPolicy::Deny;
} }
} }
if access.settings.log { if access.settings.log {
app_log(&app_req, access.settings.policy, access.settings.notify); self.app_log(&app_req, access.settings.policy, access.settings.notify);
} else if access.settings.notify { } else if access.settings.notify {
app_notify(&app_req, access.settings.policy); self.app_notify(&app_req, access.settings.policy);
} }
// Before unlocking, refresh the timestamp // Before unlocking, refresh the timestamp

View File

@@ -1,3 +1,4 @@
mod connect;
mod daemon; mod daemon;
mod db; mod db;
mod pts; mod pts;

View File

@@ -20,7 +20,7 @@ pub const DATABIN: &str = concatcp!(SECURE_DIR, "/magisk");
pub const MAGISKDB: &str = concatcp!(SECURE_DIR, "/magisk.db"); pub const MAGISKDB: &str = concatcp!(SECURE_DIR, "/magisk.db");
// tmpfs paths // tmpfs paths
const INTERNAL_DIR: &str = ".magisk"; pub const INTERNAL_DIR: &str = ".magisk";
pub const MAIN_CONFIG: &str = concatcp!(INTERNAL_DIR, "/config"); pub const MAIN_CONFIG: &str = concatcp!(INTERNAL_DIR, "/config");
pub const PREINITMIRR: &str = concatcp!(INTERNAL_DIR, "/preinit"); pub const PREINITMIRR: &str = concatcp!(INTERNAL_DIR, "/preinit");
pub const MODULEMNT: &str = concatcp!(INTERNAL_DIR, "/modules"); pub const MODULEMNT: &str = concatcp!(INTERNAL_DIR, "/modules");
@@ -37,6 +37,7 @@ pub const SEPOL_PROC_DOMAIN: &str = "magisk";
pub const MAGISK_PROC_CON: &str = concatcp!("u:r:", SEPOL_PROC_DOMAIN, ":s0"); pub const MAGISK_PROC_CON: &str = concatcp!("u:r:", SEPOL_PROC_DOMAIN, ":s0");
// Unconstrained file type that anyone can access // Unconstrained file type that anyone can access
pub const SEPOL_FILE_TYPE: &str = "magisk_file"; pub const SEPOL_FILE_TYPE: &str = "magisk_file";
pub const MAGISK_FILE_CON: &str = concatcp!("u:object_r:", SEPOL_FILE_TYPE, ":s0");
// Log pipe that only root and zygote can open // Log pipe that only root and zygote can open
pub const SEPOL_LOG_TYPE: &str = "magisk_log_file"; pub const SEPOL_LOG_TYPE: &str = "magisk_log_file";
pub const MAGISK_LOG_CON: &str = concatcp!("u:object_r:", SEPOL_LOG_TYPE, ":s0"); pub const MAGISK_LOG_CON: &str = concatcp!("u:object_r:", SEPOL_LOG_TYPE, ":s0");