Fetch policy table from Rust

This commit is contained in:
topjohnwu 2025-01-05 00:44:05 -08:00 committed by John Wu
parent a4671b4698
commit b782e7dcb7
6 changed files with 134 additions and 92 deletions

View File

@ -27,7 +27,7 @@ pub struct SqliteError(i32);
pub type SqliteResult = Result<(), SqliteError>; pub type SqliteResult = Result<(), SqliteError>;
trait SqliteReturn { pub trait SqliteReturn {
fn sql_result(self) -> SqliteResult; fn sql_result(self) -> SqliteResult;
} }
@ -41,7 +41,7 @@ impl SqliteReturn for i32 {
} }
} }
trait SqlTable { pub trait SqlTable {
fn on_row(&mut self, columns: &[String], values: &DbValues); fn on_row(&mut self, columns: &[String], values: &DbValues);
} }
@ -132,7 +132,7 @@ extern "C" {
) -> i32; ) -> i32;
} }
enum DbArg<'a> { pub enum DbArg<'a> {
Text(&'a str), Text(&'a str),
Integer(i64), Integer(i64),
} }
@ -178,7 +178,13 @@ impl MagiskD {
} }
} }
fn db_exec_with_output<T: SqlTable>(&self, sql: &str, args: &[DbArg], out: &mut T) -> i32 { fn db_exec_impl(
&self,
sql: &str,
args: &[DbArg],
exec_callback: SqlExecCallback,
exec_cookie: *mut c_void,
) -> i32 {
let mut bind_callback: SqlBindCallback = None; let mut bind_callback: SqlBindCallback = None;
let mut bind_cookie: *mut c_void = ptr::null_mut(); let mut bind_cookie: *mut c_void = ptr::null_mut();
let mut db_args = DbArgs { args, curr: 0 }; let mut db_args = DbArgs { args, curr: 0 };
@ -186,32 +192,29 @@ impl MagiskD {
bind_callback = Some(bind_arguments); bind_callback = Some(bind_arguments);
bind_cookie = (&mut db_args) as *mut DbArgs as *mut c_void; bind_cookie = (&mut db_args) as *mut DbArgs as *mut c_void;
} }
let out_ptr: *mut T = out;
self.with_db(|db| unsafe { self.with_db(|db| unsafe {
sql_exec_impl( sql_exec_impl(
db, db,
sql, sql,
bind_callback, bind_callback,
bind_cookie, bind_cookie,
Some(read_db_row::<T>), exec_callback,
out_ptr.cast(), exec_cookie,
) )
}) })
} }
fn db_exec(&self, sql: &str, args: &[DbArg]) -> i32 { pub fn db_exec_with_rows<T: SqlTable>(&self, sql: &str, args: &[DbArg], out: &mut T) -> i32 {
let mut bind_callback: SqlBindCallback = None; self.db_exec_impl(
let mut bind_cookie: *mut c_void = ptr::null_mut(); sql,
let mut db_args = DbArgs { args, curr: 0 }; args,
if !args.is_empty() { Some(read_db_row::<T>),
bind_callback = Some(bind_arguments); out as *mut T as *mut c_void,
bind_cookie = (&mut db_args) as *mut DbArgs as *mut c_void; )
} }
self.with_db(|db| unsafe { fn db_exec(&self, sql: &str, args: &[DbArg]) -> i32 {
sql_exec_impl(db, sql, bind_callback, bind_cookie, None, ptr::null_mut()) self.db_exec_impl(sql, args, None, ptr::null_mut())
})
} }
pub fn set_db_setting(&self, key: DbEntryKey, value: i32) -> SqliteResult { pub fn set_db_setting(&self, key: DbEntryKey, value: i32) -> SqliteResult {
@ -236,7 +239,7 @@ impl MagiskD {
let mut func = |_: &[String], values: &DbValues| { let mut func = |_: &[String], values: &DbValues| {
val = values.get_int(0); val = values.get_int(0);
}; };
self.db_exec_with_output( self.db_exec_with_rows(
"SELECT value FROM settings WHERE key=?", "SELECT value FROM settings WHERE key=?",
&[Text(key.to_str())], &[Text(key.to_str())],
&mut func, &mut func,
@ -249,7 +252,7 @@ impl MagiskD {
pub fn get_db_settings(&self, cfg: &mut DbSettings) -> SqliteResult { pub fn get_db_settings(&self, cfg: &mut DbSettings) -> SqliteResult {
cfg.zygisk = self.is_emulator(); cfg.zygisk = self.is_emulator();
self.db_exec_with_output("SELECT * FROM settings", &[], cfg) self.db_exec_with_rows("SELECT * FROM settings", &[], cfg)
.sql_result() .sql_result()
} }
@ -258,7 +261,7 @@ impl MagiskD {
let mut func = |_: &[String], values: &DbValues| { let mut func = |_: &[String], values: &DbValues| {
val.push_str(values.get_text(0)); val.push_str(values.get_text(0));
}; };
self.db_exec_with_output( self.db_exec_with_rows(
"SELECT value FROM strings WHERE key=?", "SELECT value FROM strings WHERE key=?",
&[Text(key.to_str())], &[Text(key.to_str())],
&mut func, &mut func,
@ -291,7 +294,7 @@ impl MagiskD {
} }
writer.ipc_write_string(&out).log().ok(); writer.ipc_write_string(&out).log().ok();
}; };
self.db_exec_with_output(&sql, &[], &mut output_fn); self.db_exec_with_rows(&sql, &[], &mut output_fn);
writer.ipc_write_string("").log() writer.ipc_write_string("").log()
} }
} }

View File

@ -13,6 +13,7 @@ use logging::{
}; };
use mount::{clean_mounts, find_preinit_device, revert_unmount, setup_mounts}; use mount::{clean_mounts, find_preinit_device, revert_unmount, setup_mounts};
use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop}; use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop};
use su::get_default_root_settings;
mod cert; mod cert;
#[path = "../include/consts.rs"] #[path = "../include/consts.rs"]
@ -22,6 +23,7 @@ mod db;
mod logging; mod logging;
mod mount; mod mount;
mod resetprop; mod resetprop;
mod su;
#[cxx::bridge] #[cxx::bridge]
pub mod ffi { pub mod ffi {
@ -121,6 +123,19 @@ pub mod ffi {
zygisk: bool, zygisk: bool,
} }
#[repr(i32)]
enum SuPolicy {
Query,
Deny,
Allow,
}
struct RootSettings {
policy: SuPolicy,
log: bool,
notify: bool,
}
unsafe extern "C++" { unsafe extern "C++" {
include!("include/sqlite.hpp"); include!("include/sqlite.hpp");
@ -165,12 +180,12 @@ pub mod ffi {
// FFI for MagiskD // FFI for MagiskD
extern "Rust" { extern "Rust" {
type MagiskD; type MagiskD;
fn setup_logfile(self: &MagiskD); fn setup_logfile(&self);
fn is_recovery(self: &MagiskD) -> bool; fn is_recovery(&self) -> bool;
fn boot_stage_handler(self: &MagiskD, client: i32, code: i32); fn boot_stage_handler(&self, client: i32, code: i32);
#[cxx_name = "get_db_settings"] #[cxx_name = "get_db_settings"]
fn get_db_settings_for_cxx(self: &MagiskD, cfg: &mut DbSettings) -> bool; fn get_db_settings_for_cxx(&self, cfg: &mut DbSettings) -> bool;
fn get_db_setting(&self, key: DbEntryKey) -> i32; fn get_db_setting(&self, key: DbEntryKey) -> i32;
#[cxx_name = "set_db_setting"] #[cxx_name = "set_db_setting"]
fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool; fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool;
@ -179,9 +194,13 @@ pub mod ffi {
fn rm_db_string_for_cxx(&self, key: DbEntryKey) -> bool; fn rm_db_string_for_cxx(&self, key: DbEntryKey) -> bool;
#[cxx_name = "db_exec"] #[cxx_name = "db_exec"]
fn db_exec_for_cxx(&self, client_fd: i32); fn db_exec_for_cxx(&self, client_fd: i32);
#[cxx_name = "get_root_settings"]
fn get_root_settings_for_cxx(&self, uid: i32, settings: &mut RootSettings) -> bool;
#[cxx_name = "DbSettings"] #[cxx_name = "DbSettings"]
fn get_default_db_settings() -> DbSettings; fn get_default_db_settings() -> DbSettings;
#[cxx_name = "RootSettings"]
fn get_default_root_settings() -> RootSettings;
#[cxx_name = "MagiskD"] #[cxx_name = "MagiskD"]
fn get_magiskd() -> &'static MagiskD; fn get_magiskd() -> &'static MagiskD;
} }

View File

@ -175,7 +175,7 @@ void app_log(const su_context &ctx) {
extras.emplace_back("from.uid", ctx.info->uid); extras.emplace_back("from.uid", ctx.info->uid);
extras.emplace_back("to.uid", static_cast<int>(ctx.req.uid)); extras.emplace_back("to.uid", static_cast<int>(ctx.req.uid));
extras.emplace_back("pid", ctx.pid); extras.emplace_back("pid", ctx.pid);
extras.emplace_back("policy", ctx.info->access.policy); extras.emplace_back("policy", +ctx.info->access.policy);
extras.emplace_back("target", ctx.req.target); extras.emplace_back("target", ctx.req.target);
extras.emplace_back("context", ctx.req.context.data()); extras.emplace_back("context", ctx.req.context.data());
extras.emplace_back("gids", &ctx.req.gids); extras.emplace_back("gids", &ctx.req.gids);
@ -193,7 +193,7 @@ void app_notify(const su_context &ctx) {
extras.reserve(3); extras.reserve(3);
extras.emplace_back("from.uid", ctx.info->uid); extras.emplace_back("from.uid", ctx.info->uid);
extras.emplace_back("pid", ctx.pid); extras.emplace_back("pid", ctx.pid);
extras.emplace_back("policy", ctx.info->access.policy); extras.emplace_back("policy", +ctx.info->access.policy);
exec_cmd("notify", extras, ctx.info); exec_cmd("notify", extras, ctx.info);
exit(0); exit(0);

60
native/src/core/su/mod.rs Normal file
View File

@ -0,0 +1,60 @@
use crate::daemon::MagiskD;
use crate::db::DbArg::Integer;
use crate::db::{SqlTable, SqliteResult, SqliteReturn};
use crate::ffi::{DbValues, RootSettings, SuPolicy};
use base::{libc, ResultExt};
use std::ptr;
impl Default for SuPolicy {
fn default() -> Self {
SuPolicy::Query
}
}
impl Default for RootSettings {
fn default() -> Self {
RootSettings {
policy: Default::default(),
log: true,
notify: true,
}
}
}
impl SqlTable for RootSettings {
fn on_row(&mut self, columns: &[String], values: &DbValues) {
for (i, column) in columns.iter().enumerate() {
let val = values.get_int(i as i32);
if column == "policy" {
self.policy.repr = val;
} else if column == "logging" {
self.log = val != 0;
} else if column == "notify" {
self.notify = val != 0;
}
}
}
}
impl MagiskD {
fn get_root_settings(&self, uid: i32, settings: &mut RootSettings) -> SqliteResult {
self.db_exec_with_rows(
"SELECT policy, logging, notification FROM policies \
WHERE uid=? AND (until=0 OR until>?)",
&[
Integer(uid as i64),
Integer(unsafe { libc::time(ptr::null_mut()).into() }),
],
settings,
)
.sql_result()
}
pub fn get_root_settings_for_cxx(&self, uid: i32, settings: &mut RootSettings) -> bool {
self.get_root_settings(uid, settings).log().is_ok()
}
}
pub fn get_default_root_settings() -> RootSettings {
RootSettings::default()
}

View File

@ -14,31 +14,8 @@
#define ATTY_OUT (1 << 1) #define ATTY_OUT (1 << 1)
#define ATTY_ERR (1 << 2) #define ATTY_ERR (1 << 2)
typedef enum { #define SILENT_ALLOW { SuPolicy::Allow, false, false }
QUERY = 0, #define SILENT_DENY { SuPolicy::Deny, false, false }
DENY = 1,
ALLOW = 2,
} policy_t;
struct su_access {
policy_t policy;
int log;
int notify;
su_access() : policy(QUERY), log(1), notify(1) {}
void operator()(StringSlice columns, const DbValues &data);
void silent_deny() {
policy = DENY;
log = 0;
notify = 0;
}
void silent_allow() {
policy = ALLOW;
log = 0;
notify = 0;
}
};
class su_info { class su_info {
public: public:
@ -48,7 +25,7 @@ public:
// These should be guarded with internal lock // These should be guarded with internal lock
int eval_uid; // The effective UID, taking multiuser settings into consideration int eval_uid; // The effective UID, taking multiuser settings into consideration
struct DbSettings cfg; struct DbSettings cfg;
su_access access; struct RootSettings access;
std::string mgr_pkg; std::string mgr_pkg;
int mgr_uid; int mgr_uid;
void check_db(); void check_db();

View File

@ -18,7 +18,7 @@ static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
static shared_ptr<su_info> cached; static shared_ptr<su_info> cached;
su_info::su_info(int uid) : su_info::su_info(int uid) :
uid(uid), eval_uid(-1), cfg(DbSettings()), uid(uid), eval_uid(-1), cfg(DbSettings()), access(RootSettings()),
mgr_uid(-1), timestamp(0), _lock(PTHREAD_MUTEX_INITIALIZER) {} mgr_uid(-1), timestamp(0), _lock(PTHREAD_MUTEX_INITIALIZER) {}
su_info::~su_info() { su_info::~su_info() {
@ -42,20 +42,6 @@ void su_info::refresh() {
timestamp = ts.tv_sec * 1000L + ts.tv_nsec / 1000000L; timestamp = ts.tv_sec * 1000L + ts.tv_nsec / 1000000L;
} }
void su_access::operator()(StringSlice columns, const DbValues &data) {
for (int i = 0; i < columns.size(); ++i) {
const auto &name = columns[i];
if (name == "policy") {
policy = (policy_t) data.get_int(i);
} else if (name == "logging") {
log = data.get_int(i);
} else if (name == "notification") {
notify = data.get_int(i);
}
}
LOGD("magiskdb: query policy=[%d] log=[%d] notify=[%d]\n", policy, log, notify);
}
void su_info::check_db() { void su_info::check_db() {
eval_uid = uid; eval_uid = uid;
MagiskD().get_db_settings(cfg); MagiskD().get_db_settings(cfg);
@ -65,7 +51,7 @@ void su_info::check_db() {
case MultiuserMode::OwnerOnly: case MultiuserMode::OwnerOnly:
if (to_user_id(uid) != 0) { if (to_user_id(uid) != 0) {
eval_uid = -1; eval_uid = -1;
access.silent_deny(); access = SILENT_DENY;
} }
break; break;
case MultiuserMode::OwnerManaged: case MultiuserMode::OwnerManaged:
@ -77,15 +63,12 @@ void su_info::check_db() {
} }
if (eval_uid > 0) { if (eval_uid > 0) {
bool res = db_exec( if (!MagiskD().get_root_settings(eval_uid, access))
"SELECT policy, logging, notification FROM policies "
"WHERE uid=? AND (until=0 OR until>?)", { eval_uid, time(nullptr) }, access);
if (!res)
return; return;
} }
// We need to check our manager // We need to check our manager
if (access.policy == QUERY || access.log || access.notify) { if (access.policy == SuPolicy::Query || access.log || access.notify) {
mgr_uid = get_manager(to_user_id(eval_uid), &mgr_pkg, true); mgr_uid = get_manager(to_user_id(eval_uid), &mgr_pkg, true);
} }
} }
@ -130,7 +113,7 @@ bool uid_granted_root(int uid) {
bool granted = false; bool granted = false;
db_exec("SELECT policy FROM policies WHERE uid=? AND (until=0 OR until>?)", db_exec("SELECT policy FROM policies WHERE uid=? AND (until=0 OR until>?)",
{ uid, time(nullptr) }, { uid, time(nullptr) },
[&](auto, const DbValues &values) { granted = values.get_int(0) == ALLOW; }); [&](auto, const DbValues &values) { granted = values.get_int(0) == +SuPolicy::Allow; });
return granted; return granted;
} }
@ -167,7 +150,7 @@ void prune_su_access() {
static shared_ptr<su_info> get_su_info(unsigned uid) { static shared_ptr<su_info> get_su_info(unsigned uid) {
if (uid == AID_ROOT) { if (uid == AID_ROOT) {
auto info = make_shared<su_info>(uid); auto info = make_shared<su_info>(uid);
info->access.silent_allow(); info->access = SILENT_ALLOW;
return info; return info;
} }
@ -182,13 +165,13 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
mutex_guard lock = info->lock(); mutex_guard lock = info->lock();
if (info->access.policy == QUERY) { if (info->access.policy == SuPolicy::Query) {
// Not cached, get data from database // Not cached, get data from database
info->check_db(); info->check_db();
// If it's the manager, allow it silently // If it's the manager, allow it silently
if (to_app_id(info->uid) == to_app_id(info->mgr_uid)) { if (to_app_id(info->uid) == to_app_id(info->mgr_uid)) {
info->access.silent_allow(); info->access = SILENT_ALLOW;
return info; return info;
} }
@ -196,18 +179,18 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
switch (info->cfg.root_access) { switch (info->cfg.root_access) {
case RootAccess::Disabled: case RootAccess::Disabled:
LOGW("Root access is disabled!\n"); LOGW("Root access is disabled!\n");
info->access.silent_deny(); info->access = SILENT_DENY;
break; break;
case RootAccess::AdbOnly: case RootAccess::AdbOnly:
if (info->uid != AID_SHELL) { if (info->uid != AID_SHELL) {
LOGW("Root access limited to ADB only!\n"); LOGW("Root access limited to ADB only!\n");
info->access.silent_deny(); info->access = SILENT_DENY;
} }
break; break;
case RootAccess::AppsOnly: case RootAccess::AppsOnly:
if (info->uid == AID_SHELL) { if (info->uid == AID_SHELL) {
LOGW("Root access is disabled for ADB!\n"); LOGW("Root access is disabled for ADB!\n");
info->access.silent_deny(); info->access = SILENT_DENY;
} }
break; break;
case RootAccess::AppsAndAdb: case RootAccess::AppsAndAdb:
@ -215,12 +198,12 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
break; break;
} }
if (info->access.policy != QUERY) if (info->access.policy != SuPolicy::Query)
return info; return info;
// If still not determined, check if manager exists // If still not determined, check if manager exists
if (info->mgr_uid < 0) { if (info->mgr_uid < 0) {
info->access.silent_deny(); info->access = SILENT_DENY;
return info; return info;
} }
} }
@ -266,19 +249,19 @@ void su_daemon_handler(int client, const sock_cred *cred) {
|| !read_vector(client, ctx.req.gids)) { || !read_vector(client, ctx.req.gids)) {
LOGW("su: remote process probably died, abort\n"); LOGW("su: remote process probably died, abort\n");
ctx.info.reset(); ctx.info.reset();
write_int(client, DENY); write_int(client, +SuPolicy::Deny);
close(client); close(client);
return; return;
} }
// If still not determined, ask manager // If still not determined, ask manager
if (ctx.info->access.policy == QUERY) { if (ctx.info->access.policy == SuPolicy::Query) {
int fd = app_request(ctx); int fd = app_request(ctx);
if (fd < 0) { if (fd < 0) {
ctx.info->access.policy = DENY; ctx.info->access.policy = SuPolicy::Deny;
} else { } else {
int ret = read_int_be(fd); int ret = read_int_be(fd);
ctx.info->access.policy = ret < 0 ? DENY : static_cast<policy_t>(ret); ctx.info->access.policy = ret < 0 ? SuPolicy::Deny : static_cast<SuPolicy>(ret);
close(fd); close(fd);
} }
} }
@ -289,10 +272,10 @@ void su_daemon_handler(int client, const sock_cred *cred) {
app_notify(ctx); app_notify(ctx);
// Fail fast // Fail fast
if (ctx.info->access.policy == DENY) { if (ctx.info->access.policy == SuPolicy::Deny) {
LOGW("su: request rejected (%u)\n", ctx.info->uid); LOGW("su: request rejected (%u)\n", ctx.info->uid);
ctx.info.reset(); ctx.info.reset();
write_int(client, DENY); write_int(client, +SuPolicy::Deny);
close(client); close(client);
return; return;
} }