Bind SQLite to Rust

This commit is contained in:
topjohnwu 2025-01-03 11:38:15 -08:00 committed by John Wu
parent 8e1a44e7eb
commit 24650eefe4
17 changed files with 765 additions and 521 deletions

View File

@ -20,7 +20,6 @@ LOCAL_SRC_FILES := \
core/daemon.cpp \
core/bootstages.cpp \
core/socket.cpp \
core/db.cpp \
core/package.cpp \
core/scripting.cpp \
core/selinux.cpp \

1
native/src/Cargo.lock generated
View File

@ -449,6 +449,7 @@ dependencies = [
"num-traits",
"pb-rs",
"quick-protobuf",
"thiserror",
]
[[package]]

View File

@ -340,6 +340,11 @@ impl Utf8CStr {
Self::from_cstr(unsafe { CStr::from_ptr(ptr) })
}
pub unsafe fn from_ptr_unchecked<'a>(ptr: *const c_char) -> &'a Utf8CStr {
let cstr = CStr::from_ptr(ptr);
Self::from_bytes_unchecked(cstr.to_bytes_with_nul())
}
#[inline(always)]
pub fn as_bytes_with_nul(&self) -> &[u8] {
&self.0

View File

@ -18,3 +18,4 @@ num-traits = { workspace = true }
num-derive = { workspace = true }
quick-protobuf = { workspace = true }
bytemuck = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }

View File

@ -7,7 +7,7 @@
#include <string>
#include <consts.hpp>
#include <db.hpp>
#include <sqlite.hpp>
#include <base.hpp>
#include <core.hpp>
#include <selinux.hpp>
@ -133,17 +133,6 @@ static bool check_key_combo() {
return true;
}
static bool check_safe_mode() {
int bootloop_cnt;
db_settings dbs;
get_db_settings(dbs, BOOTLOOP_COUNT);
bootloop_cnt = dbs.bootloop;
// Increment the bootloop counter
set_db_settings(BOOTLOOP_COUNT, bootloop_cnt + 1);
return bootloop_cnt >= 2 || get_prop("persist.sys.safemode", true) == "1" ||
get_prop("ro.sys.safemode") == "1" || check_key_combo();
}
/***********************
* Boot Stage Handlers *
***********************/
@ -155,15 +144,12 @@ bool MagiskD::post_fs_data() const noexcept {
preserve_stub_apk();
bool safe_mode = false;
if (access(SECURE_DIR, F_OK) != 0) {
if (SDK_INT < 24) {
xmkdir(SECURE_DIR, 0700);
} else {
LOGE(SECURE_DIR " is not present, abort\n");
safe_mode = true;
return safe_mode;
return true;
}
}
@ -171,28 +157,31 @@ bool MagiskD::post_fs_data() const noexcept {
if (!magisk_env()) {
LOGE("* Magisk environment incomplete, abort\n");
safe_mode = true;
return safe_mode;
return true;
}
if (check_safe_mode()) {
// Check safe mode
int bootloop_cnt = get_db_setting(DbEntryKey::BootloopCount);
// Increment the boot counter
set_db_setting(DbEntryKey::BootloopCount, bootloop_cnt + 1);
bool safe_mode = bootloop_cnt >= 2 || get_prop("persist.sys.safemode", true) == "1" ||
get_prop("ro.sys.safemode") == "1" || check_key_combo();
if (safe_mode) {
LOGI("* Safe mode triggered\n");
safe_mode = true;
// Disable all modules and zygisk so next boot will be clean
disable_modules();
set_db_settings(ZYGISK_CONFIG, false);
return safe_mode;
set_db_setting(DbEntryKey::ZygiskConfig, false);
return true;
}
exec_common_scripts("post-fs-data");
db_settings dbs;
get_db_settings(dbs, ZYGISK_CONFIG);
zygisk_enabled = dbs.zygisk;
zygisk_enabled = get_db_setting(DbEntryKey::ZygiskConfig);
initialize_denylist();
setup_mounts();
handle_modules();
load_modules();
return safe_mode;
return false;
}
void MagiskD::late_start() const noexcept {
@ -210,7 +199,7 @@ void MagiskD::boot_complete() const noexcept {
LOGI("** boot-complete triggered\n");
// Reset the bootloop counter once we have boot-complete
set_db_settings(BOOTLOOP_COUNT, 0);
set_db_setting(DbEntryKey::BootloopCount, 0);
// At this point it's safe to create the folder
if (access(SECURE_DIR, F_OK) != 0)

View File

@ -7,7 +7,7 @@
#include <base.hpp>
#include <core.hpp>
#include <selinux.hpp>
#include <db.hpp>
#include <sqlite.hpp>
#include <flags.h>
using namespace std;
@ -151,7 +151,7 @@ static void handle_request_async(int client, int code, const sock_cred &cred) {
close(client);
break;
case +RequestCode::SQLITE_CMD:
exec_sql(client);
MagiskD().db_exec(client);
break;
case +RequestCode::REMOVE_MODULES: {
int do_reboot = read_int(client);

View File

@ -1,15 +1,16 @@
use std::fs::File;
use std::io;
use std::io::BufReader;
use std::sync::{Mutex, OnceLock};
use base::libc::{O_CLOEXEC, O_RDONLY};
use base::{
cstr, libc, open_fd, BufReadExt, Directory, FsPathBuf, ResultExt, Utf8CStr, Utf8CStrBuf,
Utf8CStrBufArr, Utf8CStrBufRef, WalkResult,
cstr, libc, open_fd, BufReadExt, Directory, FsPathBuf, ReadExt, ResultExt, Utf8CStr,
Utf8CStrBuf, Utf8CStrBufArr, Utf8CStrBufRef, WalkResult,
};
use bytemuck::bytes_of;
use std::fs::File;
use std::io;
use std::io::{BufReader, Read, Write};
use std::sync::{Mutex, OnceLock};
use crate::consts::MAIN_CONFIG;
use crate::db::Sqlite3;
use crate::ffi::{get_magisk_tmp, RequestCode};
use crate::get_prop;
use crate::logging::magisk_logging;
@ -42,6 +43,7 @@ impl BootStateFlags {
#[derive(Default)]
pub struct MagiskD {
pub logd: Mutex<Option<File>>,
pub sql_connection: Mutex<Option<Sqlite3>>,
boot_stage_lock: Mutex<BootStateFlags>,
is_emulator: bool,
is_recovery: bool,
@ -187,3 +189,39 @@ pub fn find_apk_path(pkg: &Utf8CStr, data: &mut [u8]) -> usize {
.log()
.unwrap_or(0)
}
pub trait IpcRead {
fn ipc_read_int(&mut self) -> io::Result<i32>;
fn ipc_read_string(&mut self) -> io::Result<String>;
}
impl<T: Read> IpcRead for T {
fn ipc_read_int(&mut self) -> io::Result<i32> {
let mut val: i32 = 0;
self.read_pod(&mut val)?;
Ok(val)
}
fn ipc_read_string(&mut self) -> io::Result<String> {
let len = self.ipc_read_int()?;
let mut val = "".to_string();
self.take(len as u64).read_to_string(&mut val)?;
Ok(val)
}
}
pub trait IpcWrite {
fn ipc_write_int(&mut self, val: i32) -> io::Result<()>;
fn ipc_write_string(&mut self, val: &str) -> io::Result<()>;
}
impl<T: Write> IpcWrite for T {
fn ipc_write_int(&mut self, val: i32) -> io::Result<()> {
self.write_all(bytes_of(&val))
}
fn ipc_write_string(&mut self, val: &str) -> io::Result<()> {
self.ipc_write_int(val.len() as i32)?;
self.write_all(val.as_bytes())
}
}

View File

@ -1,300 +0,0 @@
#include <unistd.h>
#include <sys/stat.h>
#include <consts.hpp>
#include <base.hpp>
#include <db.hpp>
#include <sqlite.hpp>
#include <core.hpp>
#define DB_VERSION 12
#define DB_VERSION_STR "12"
using namespace std;
#define DBLOGV(...)
//#define DBLOGV(...) LOGD("magiskdb: " __VA_ARGS__)
#define sql_chk_log(fn, ...) if (int rc = fn(__VA_ARGS__); rc != SQLITE_OK) { \
LOGE("sqlite3(db.cpp:%d): %s\n", __LINE__, sqlite3_errstr(rc)); \
return false; \
}
static bool open_and_init_db_impl(sqlite3 **dbOut) {
if (!load_sqlite()) {
LOGE("sqlite3: Cannot load libsqlite.so\n");
return false;
}
unique_ptr<sqlite3, decltype(sqlite3_close)> db(nullptr, sqlite3_close);
{
sqlite3 *sql;
sql_chk_log(sqlite3_open_v2, MAGISKDB, &sql,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr);
db.reset(sql);
}
int ver = 0;
bool upgrade = false;
auto ver_cb = [](void *ver, auto, DbValues &data) {
*static_cast<int *>(ver) = data.get_int(0);
};
sql_chk_log(sql_exec, db.get(), "PRAGMA user_version", nullptr, nullptr, ver_cb, &ver);
if (ver > DB_VERSION) {
// Don't support downgrading database
LOGE("sqlite3: Downgrading database is not supported\n");
return false;
}
auto create_policy = [&] {
return sql_exec(db.get(),
"CREATE TABLE IF NOT EXISTS policies "
"(uid INT, policy INT, until INT, logging INT, "
"notification INT, PRIMARY KEY(uid))");
};
auto create_settings = [&] {
return sql_exec(db.get(),
"CREATE TABLE IF NOT EXISTS settings "
"(key TEXT, value INT, PRIMARY KEY(key))");
};
auto create_strings = [&] {
return sql_exec(db.get(),
"CREATE TABLE IF NOT EXISTS strings "
"(key TEXT, value TEXT, PRIMARY KEY(key))");
};
auto create_denylist = [&] {
return sql_exec(db.get(),
"CREATE TABLE IF NOT EXISTS denylist "
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process))");
};
// Database changelog:
//
// 0 - 6: DB stored in app private data. There are no longer any code in the project to
// migrate these data, so no need to take any of these versions into consideration.
// 7 : create table `hidelist` (process TEXT, PRIMARY KEY(process))
// 8 : add new column (package_name TEXT) to table `hidelist`
// 9 : rebuild table `hidelist` to change primary key (PRIMARY KEY(package_name, process))
// 10: remove table `logs`
// 11: remove table `hidelist` and create table `denylist` (same data structure)
// 12: rebuild table `policies` to drop column `package_name`
if (/* 0, 1, 2, 3, 4, 5, 6 */ ver <= 6) {
sql_chk_log(create_policy);
sql_chk_log(create_settings);
sql_chk_log(create_strings);
sql_chk_log(create_denylist);
// Directly jump to latest
ver = DB_VERSION;
upgrade = true;
}
if (ver == 7) {
sql_chk_log(sql_exec, db.get(),
"BEGIN TRANSACTION;"
"ALTER TABLE hidelist RENAME TO hidelist_tmp;"
"CREATE TABLE IF NOT EXISTS hidelist "
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));"
"INSERT INTO hidelist SELECT process as package_name, process FROM hidelist_tmp;"
"DROP TABLE hidelist_tmp;"
"COMMIT;");
// Directly jump to version 9
ver = 9;
upgrade = true;
}
if (ver == 8) {
sql_chk_log(sql_exec, db.get(),
"BEGIN TRANSACTION;"
"ALTER TABLE hidelist RENAME TO hidelist_tmp;"
"CREATE TABLE IF NOT EXISTS hidelist "
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));"
"INSERT INTO hidelist SELECT * FROM hidelist_tmp;"
"DROP TABLE hidelist_tmp;"
"COMMIT;");
ver = 9;
upgrade = true;
}
if (ver == 9) {
sql_chk_log(sql_exec, db.get(), "DROP TABLE IF EXISTS logs", nullptr, nullptr);
ver = 10;
upgrade = true;
}
if (ver == 10) {
sql_chk_log(sql_exec, db.get(),
"DROP TABLE IF EXISTS hidelist;"
"DELETE FROM settings WHERE key='magiskhide';");
sql_chk_log(create_denylist);
ver = 11;
upgrade = true;
}
if (ver == 11) {
sql_chk_log(sql_exec, db.get(),
"BEGIN TRANSACTION;"
"ALTER TABLE policies RENAME TO policies_tmp;"
"CREATE TABLE IF NOT EXISTS policies "
"(uid INT, policy INT, until INT, logging INT, "
"notification INT, PRIMARY KEY(uid));"
"INSERT INTO policies "
"SELECT uid, policy, until, logging, notification FROM policies_tmp;"
"DROP TABLE policies_tmp;"
"COMMIT;");
ver = 12;
upgrade = true;
}
if (upgrade) {
// Set version
sql_chk_log(sql_exec, db.get(), "PRAGMA user_version=" DB_VERSION_STR);
}
*dbOut = db.release();
return true;
}
sqlite3 *open_and_init_db() {
sqlite3 *db = nullptr;
return open_and_init_db_impl(&db) ? db : nullptr;
}
static sqlite3 *get_db() {
static sqlite3 *db = nullptr;
if (db == nullptr) {
db = open_and_init_db();
if (db == nullptr) {
// Open failed, remove and reconstruct
unlink(MAGISKDB);
db = open_and_init_db();
}
}
return db;
}
bool db_exec(const char *sql, DbArgs args, db_exec_callback exec_fn) {
using db_bind_callback = std::function<int(int, DbStatement&)>;
if (sqlite3 *db = get_db()) {
db_bind_callback bind_fn = {};
sql_bind_callback bind_cb = nullptr;
if (!args.empty()) {
bind_fn = std::ref(args);
bind_cb = [](void *v, int index, DbStatement &stmt) -> int {
auto fn = static_cast<db_bind_callback*>(v);
return fn->operator()(index, stmt);
};
}
sql_exec_callback exec_cb = nullptr;
if (exec_fn) {
exec_cb = [](void *v, StringSlice columns, DbValues &data) {
auto fn = static_cast<db_exec_callback*>(v);
fn->operator()(columns, data);
};
}
sql_chk_log(sql_exec, db, sql, bind_cb, &bind_fn, exec_cb, &exec_fn);
return true;
}
return false;
}
bool get_db_settings(db_settings &cfg, int key) {
if (key >= 0) {
return db_exec("SELECT * FROM settings WHERE key=?", { DB_SETTING_KEYS[key] }, cfg);
} else {
return db_exec("SELECT * FROM settings", {}, cfg);
}
}
bool set_db_settings(int key, int value) {
return db_exec(
"INSERT OR REPLACE INTO settings (key,value) VALUES(?,?)",
{ DB_SETTING_KEYS[key], value });
}
bool get_db_strings(db_strings &str, int key) {
if (key >= 0) {
return db_exec("SELECT * FROM strings WHERE key=?", { DB_STRING_KEYS[key] }, str);
} else {
return db_exec("SELECT * FROM strings", {}, str);
}
}
bool rm_db_strings(int key) {
return db_exec("DELETE FROM strings WHERE key=?", { DB_STRING_KEYS[key] });
}
void exec_sql(owned_fd client) {
string sql = read_string(client);
db_exec(sql.data(), {}, [fd = (int) client](StringSlice columns, DbValues &data) {
string out;
for (int i = 0; i < columns.size(); ++i) {
if (i != 0) out += '|';
out += columns[i].c_str();
out += '=';
out += data.get_text(i);
}
write_string(fd, out);
});
write_int(client, 0);
}
db_settings::db_settings() :
root_access(ROOT_ACCESS_APPS_AND_ADB),
multiuser_mode(MULTIUSER_MODE_OWNER_ONLY),
mnt_ns(NAMESPACE_MODE_REQUESTER),
bootloop(0),
denylist(false),
zygisk(MagiskD().is_emulator()) {}
void db_settings::operator()(StringSlice columns, DbValues &data) {
string_view key;
int val;
for (int i = 0; i < columns.size(); ++i) {
const auto &name = columns[i];
if (name == "key") {
key = data.get_text(i);
} else if (name == "value") {
val = data.get_int(i);
}
}
if (key == DB_SETTING_KEYS[ROOT_ACCESS]) {
root_access = val;
} else if (key == DB_SETTING_KEYS[SU_MULTIUSER_MODE]) {
multiuser_mode = val;
} else if (key == DB_SETTING_KEYS[SU_MNT_NS]) {
mnt_ns = val;
} else if (key == DB_SETTING_KEYS[BOOTLOOP_COUNT]) {
bootloop = val;
} else if (key == DB_SETTING_KEYS[DENYLIST_CONFIG]) {
denylist = val;
} else if (key == DB_SETTING_KEYS[ZYGISK_CONFIG]) {
zygisk = val;
}
}
void db_strings::operator()(StringSlice columns, DbValues &data) {
string_view key;
const char *val;
for (int i = 0; i < columns.size(); ++i) {
const auto &name = columns[i];
if (name == "key") {
key = data.get_text(i);
} else if (name == "value") {
val = data.get_text(i);
}
}
if (key == DB_STRING_KEYS[SU_MANAGER]) {
su_manager = val;
}
}
int DbArgs::operator()(int index, DbStatement &stmt) {
if (curr < args.size()) {
const auto &arg = args[curr++];
switch (arg.type) {
case DbArg::INT:
return stmt.bind_int64(index, arg.int_val);
case DbArg::TEXT:
return stmt.bind_text(index, arg.str_val);
}
}
return SQLITE_OK;
}

337
native/src/core/db.rs Normal file
View File

@ -0,0 +1,337 @@
#![allow(improper_ctypes, improper_ctypes_definitions)]
use crate::daemon::{IpcRead, IpcWrite, MagiskD, MAGISKD};
use crate::ffi::{
open_and_init_db, sqlite3, sqlite3_errstr, DbEntryKey, DbSettings, DbStatement, DbValues,
MntNsMode, MultiuserMode, RootAccess,
};
use base::{LoggedResult, ResultExt, Utf8CStr};
use std::ffi::c_void;
use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::os::fd::{FromRawFd, OwnedFd, RawFd};
use std::pin::Pin;
use std::ptr;
use std::ptr::NonNull;
use thiserror::Error;
use DbArg::{Integer, Text};
fn sqlite_err_str(code: i32) -> &'static Utf8CStr {
// SAFETY: sqlite3 always returns UTF-8 strings
unsafe { Utf8CStr::from_ptr_unchecked(sqlite3_errstr(code)) }
}
#[repr(transparent)]
#[derive(Error, Debug)]
#[error("sqlite3: {}", sqlite_err_str(self.0))]
pub struct SqliteError(i32);
pub type SqliteResult = Result<(), SqliteError>;
trait SqliteReturn {
fn sql_result(self) -> SqliteResult;
}
impl SqliteReturn for i32 {
fn sql_result(self) -> SqliteResult {
if self != 0 {
Err(SqliteError(self))
} else {
Ok(())
}
}
}
trait SqlTable {
fn on_row(&mut self, columns: &[String], data: &DbValues);
}
impl<T> SqlTable for T
where
T: FnMut(&[String], &DbValues),
{
fn on_row(&mut self, columns: &[String], data: &DbValues) {
self.call_mut((columns, data))
}
}
impl Default for RootAccess {
fn default() -> Self {
RootAccess::AppsAndAdb
}
}
impl Default for MultiuserMode {
fn default() -> Self {
MultiuserMode::OwnerOnly
}
}
impl Default for MntNsMode {
fn default() -> Self {
MntNsMode::Requester
}
}
pub fn get_default_db_settings() -> DbSettings {
DbSettings::default()
}
impl DbEntryKey {
fn to_str(self) -> &'static str {
match self {
DbEntryKey::RootAccess => "root_access",
DbEntryKey::SuMultiuserMode => "multiuser_mode",
DbEntryKey::SuMntNs => "mnt_ns",
DbEntryKey::DenylistConfig => "denylist",
DbEntryKey::ZygiskConfig => "zygisk",
DbEntryKey::BootloopCount => "bootloop",
DbEntryKey::SuManager => "requester",
_ => "",
}
}
}
impl SqlTable for DbSettings {
fn on_row(&mut self, columns: &[String], data: &DbValues) {
let mut key = "";
let mut value = 0;
for (i, column) in columns.iter().enumerate() {
if column == "key" {
key = data.get_text(i as i32);
} else if column == "value" {
value = data.get_int(i as i32);
}
}
match key {
"root_access" => self.root_access = RootAccess { repr: value },
"multiuser_mode" => self.multiuser_mode = MultiuserMode { repr: value },
"mnt_ns" => self.mnt_ns = MntNsMode { repr: value },
"denylist" => self.denylist = value != 0,
"zygisk" => self.zygisk = value != 0,
"bootloop" => self.boot_count = value,
_ => {}
}
}
}
#[repr(transparent)]
pub struct Sqlite3(NonNull<sqlite3>);
unsafe impl Send for Sqlite3 {}
type SqlBindCallback = Option<unsafe extern "C" fn(*mut c_void, i32, Pin<&mut DbStatement>) -> i32>;
type SqlExecCallback = Option<unsafe extern "C" fn(*mut c_void, &[String], &DbValues)>;
extern "C" {
fn sql_exec_impl(
db: *mut sqlite3,
sql: &str,
bind_callback: SqlBindCallback,
bind_cookie: *mut c_void,
exec_callback: SqlExecCallback,
exec_cookie: *mut c_void,
) -> i32;
}
enum DbArg<'a> {
Text(&'a str),
Integer(i64),
}
struct DbArgs<'a> {
args: &'a [DbArg<'a>],
curr: usize,
}
unsafe extern "C" fn bind_arguments(v: *mut c_void, idx: i32, stmt: Pin<&mut DbStatement>) -> i32 {
let args = &mut *(v as *mut DbArgs<'_>);
if args.curr < args.args.len() {
let arg = &args.args[args.curr];
match *arg {
Text(v) => stmt.bind_text(idx, v),
Integer(v) => stmt.bind_int64(idx, v),
}
} else {
0
}
}
unsafe extern "C" fn read_db_row<T: SqlTable>(
v: *mut c_void,
columns: &[String],
values: &DbValues,
) {
let table = &mut *(v as *mut T);
table.on_row(columns, values);
}
impl MagiskD {
fn with_db<F: FnOnce(*mut sqlite3) -> i32>(&self, f: F) -> i32 {
let mut db = self.sql_connection.lock().unwrap();
if db.is_none() {
let raw_db = open_and_init_db();
*db = NonNull::new(raw_db).map(Sqlite3);
}
if let Some(ref mut db) = *db {
f(db.0.as_ptr())
} else {
-1
}
}
fn db_exec_with_output<T: SqlTable>(&self, sql: &str, args: &[DbArg], out: &mut T) -> i32 {
let mut bind_callback: SqlBindCallback = None;
let mut bind_cookie: *mut c_void = ptr::null_mut();
let mut db_args = DbArgs { args, curr: 0 };
if !args.is_empty() {
bind_callback = Some(bind_arguments);
bind_cookie = (&mut db_args) as *mut DbArgs as *mut c_void;
}
let out_ptr: *mut T = out;
self.with_db(|db| unsafe {
sql_exec_impl(
db,
sql,
bind_callback,
bind_cookie,
Some(read_db_row::<T>),
out_ptr.cast(),
)
})
}
fn db_exec(&self, sql: &str, args: &[DbArg]) -> i32 {
let mut bind_callback: SqlBindCallback = None;
let mut bind_cookie: *mut c_void = ptr::null_mut();
let mut db_args = DbArgs { args, curr: 0 };
if !args.is_empty() {
bind_callback = Some(bind_arguments);
bind_cookie = (&mut db_args) as *mut DbArgs as *mut c_void;
}
self.with_db(|db| unsafe {
sql_exec_impl(db, sql, bind_callback, bind_cookie, None, ptr::null_mut())
})
}
pub fn set_db_setting(&self, key: DbEntryKey, value: i32) -> SqliteResult {
self.db_exec(
"INSERT OR REPLACE INTO settings (key,value) VALUES(?,?)",
&[Text(key.to_str()), Integer(value as i64)],
)
.sql_result()
}
pub fn get_db_setting(&self, key: DbEntryKey) -> i32 {
// Get default values
let mut val = match key {
DbEntryKey::RootAccess => RootAccess::default().repr,
DbEntryKey::SuMultiuserMode => MultiuserMode::default().repr,
DbEntryKey::SuMntNs => MntNsMode::default().repr,
DbEntryKey::DenylistConfig => 0,
DbEntryKey::ZygiskConfig => self.is_emulator() as i32,
DbEntryKey::BootloopCount => 0,
_ => -1,
};
let mut func = |_: &[String], values: &DbValues| {
val = values.get_int(0);
};
self.db_exec_with_output(
"SELECT value FROM settings WHERE key=?",
&[Text(key.to_str())],
&mut func,
)
.sql_result()
.log()
.ok();
val
}
pub fn get_db_settings(&self, cfg: &mut DbSettings) -> SqliteResult {
cfg.zygisk = self.is_emulator();
self.db_exec_with_output("SELECT * FROM settings", &[], cfg)
.sql_result()
}
pub fn get_db_string(&self, key: DbEntryKey) -> String {
let mut val = "".to_string();
let mut func = |_: &[String], values: &DbValues| {
val.push_str(values.get_text(0));
};
self.db_exec_with_output(
"SELECT value FROM strings WHERE key=?",
&[Text(key.to_str())],
&mut func,
)
.sql_result()
.log()
.ok();
val
}
pub fn rm_db_string(&self, key: DbEntryKey) -> SqliteResult {
self.db_exec("DELETE FROM strings WHERE key=?", &[Text(key.to_str())])
.sql_result()
}
fn db_exec_for_client(&self, fd: OwnedFd) -> LoggedResult<()> {
let mut file = File::from(fd);
let mut reader = BufReader::new(&mut file);
let sql = reader.ipc_read_string()?;
let mut writer = BufWriter::new(&mut file);
let mut output_fn = |columns: &[String], values: &DbValues| {
let mut out = "".to_string();
for (i, column) in columns.iter().enumerate() {
if i != 0 {
out.push('|');
}
out.push_str(column);
out.push('=');
out.push_str(values.get_text(i as i32));
}
writer.ipc_write_string(&out).log().ok();
};
self.db_exec_with_output(&sql, &[], &mut output_fn);
writer.ipc_write_string("").log()
}
}
impl MagiskD {
pub fn get_db_settings_for_cxx(&self, cfg: &mut DbSettings) -> bool {
self.get_db_settings(cfg).log().is_ok()
}
pub fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool {
self.set_db_setting(key, value).log().is_ok()
}
pub fn rm_db_string_for_cxx(&self, key: DbEntryKey) -> bool {
self.rm_db_string(key).log().is_ok()
}
pub fn db_exec_for_cxx(&self, client_fd: RawFd) {
// Take ownership
let fd = unsafe { OwnedFd::from_raw_fd(client_fd) };
self.db_exec_for_client(fd).ok();
}
}
#[export_name = "sql_exec_rs"]
unsafe extern "C" fn sql_exec_for_cxx(
sql: &str,
bind_callback: SqlBindCallback,
bind_cookie: *mut c_void,
exec_callback: SqlExecCallback,
exec_cookie: *mut c_void,
) -> i32 {
MAGISKD.get().unwrap_unchecked().with_db(|db| {
sql_exec_impl(
db,
sql,
bind_callback,
bind_cookie,
exec_callback,
exec_cookie,
)
})
}

View File

@ -8,7 +8,7 @@
#include <consts.hpp>
#include <base.hpp>
#include <db.hpp>
#include <sqlite.hpp>
#include <core.hpp>
#include "deny.hpp"
@ -385,7 +385,7 @@ int enable_deny() {
}
}
set_db_settings(DENYLIST_CONFIG, true);
MagiskD().set_db_setting(DbEntryKey::DenylistConfig, true);
return DenyResponse::OK;
}
@ -393,15 +393,13 @@ int disable_deny() {
if (denylist_enforced.exchange(false)) {
LOGI("* Disable DenyList\n");
}
set_db_settings(DENYLIST_CONFIG, false);
MagiskD().set_db_setting(DbEntryKey::DenylistConfig, false);
return DenyResponse::OK;
}
void initialize_denylist() {
if (!denylist_enforced) {
db_settings dbs;
get_db_settings(dbs, DENYLIST_CONFIG);
if (dbs.denylist)
if (MagiskD().get_db_setting(DbEntryKey::DenylistConfig))
enable_deny();
}
}

View File

@ -1,128 +0,0 @@
#pragma once
#include <sys/stat.h>
#include <map>
#include <string>
#include <string_view>
#include <functional>
#include <base.hpp>
#include <sqlite.hpp>
/***************
* DB Settings *
***************/
constexpr const char *DB_SETTING_KEYS[] = {
"root_access",
"multiuser_mode",
"mnt_ns",
"denylist",
"zygisk",
"bootloop",
};
// Settings key indices
enum {
ROOT_ACCESS = 0,
SU_MULTIUSER_MODE,
SU_MNT_NS,
DENYLIST_CONFIG,
ZYGISK_CONFIG,
BOOTLOOP_COUNT,
};
// Values for root_access
enum {
ROOT_ACCESS_DISABLED = 0,
ROOT_ACCESS_APPS_ONLY,
ROOT_ACCESS_ADB_ONLY,
ROOT_ACCESS_APPS_AND_ADB
};
// Values for multiuser_mode
enum {
MULTIUSER_MODE_OWNER_ONLY = 0,
MULTIUSER_MODE_OWNER_MANAGED,
MULTIUSER_MODE_USER
};
// Values for mnt_ns
enum {
NAMESPACE_MODE_GLOBAL = 0,
NAMESPACE_MODE_REQUESTER,
NAMESPACE_MODE_ISOLATE
};
struct db_settings {
int root_access;
int multiuser_mode;
int mnt_ns;
int bootloop;
bool denylist;
bool zygisk;
db_settings();
void operator()(StringSlice columns, DbValues &data);
};
/**************
* DB Strings *
**************/
constexpr const char *DB_STRING_KEYS[] = { "requester" };
// Strings keys indices
enum {
SU_MANAGER = 0
};
struct db_strings {
std::string su_manager;
void operator()(StringSlice columns, DbValues &data);
};
/********************
* Public Functions *
********************/
using db_exec_callback = std::function<void(StringSlice, DbValues&)>;
struct DbArg {
enum {
INT,
TEXT,
} type;
union {
int64_t int_val;
rust::Str str_val;
};
DbArg(int64_t v) : type(INT), int_val(v) {}
DbArg(const char *v) : type(TEXT), str_val(v) {}
};
struct DbArgs {
DbArgs() : curr(0) {}
DbArgs(std::initializer_list<DbArg> list) : args(list), curr(0) {}
int operator()(int index, DbStatement &stmt);
bool empty() const { return args.empty(); }
private:
std::vector<DbArg> args;
size_t curr;
};
bool get_db_settings(db_settings &cfg, int key = -1);
bool set_db_settings(int key, int value);
bool get_db_strings(db_strings &str, int key = -1);
bool rm_db_strings(int key);
void exec_sql(owned_fd client);
bool db_exec(const char *sql, DbArgs args = {}, db_exec_callback exec_fn = {});
template<typename T>
concept DbData = requires(T t, StringSlice s, DbValues &v) { t(s, v); };
template<DbData T>
bool db_exec(const char *sql, DbArgs args, T &data) {
return db_exec(sql, std::move(args), (db_exec_callback) std::ref(data));
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <functional>
#include <cxx.h>
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
@ -13,14 +15,13 @@
struct sqlite3;
struct sqlite3_stmt;
extern int (*sqlite3_open_v2)(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);
extern int (*sqlite3_close)(sqlite3 *db);
extern const char *(*sqlite3_errstr)(int);
// Transparent wrappers of sqlite3_stmt
struct DbValues {
const char *get_text(int index);
int get_int(int index);
const char *get_text(int index) const;
rust::Str get_str(int index) const { return get_text(index); }
int get_int(int index) const;
~DbValues() = delete;
};
struct DbStatement {
@ -33,8 +34,43 @@ using StringSlice = rust::Slice<rust::String>;
using sql_bind_callback = int(*)(void*, int, DbStatement&);
using sql_exec_callback = void(*)(void*, StringSlice, DbValues&);
bool load_sqlite();
sqlite3 *open_and_init_db();
int sql_exec(sqlite3 *db, rust::Str zSql,
sql_bind_callback bind_cb = nullptr, void *bind_cookie = nullptr,
sql_exec_callback exec_cb = nullptr, void *exec_cookie = nullptr);
/************
* C++ APIs *
************/
using db_exec_callback = std::function<void(StringSlice, DbValues&)>;
struct DbArg {
enum {
INT,
TEXT,
} type;
union {
int64_t int_val;
rust::Str str_val;
};
DbArg(int64_t v) : type(INT), int_val(v) {}
DbArg(const char *v) : type(TEXT), str_val(v) {}
};
struct DbArgs {
DbArgs() : curr(0) {}
DbArgs(std::initializer_list<DbArg> list) : args(list), curr(0) {}
int operator()(int index, DbStatement &stmt);
bool empty() const { return args.empty(); }
private:
std::vector<DbArg> args;
size_t curr;
};
bool db_exec(const char *sql, DbArgs args = {}, db_exec_callback exec_fn = {});
template<typename T>
concept DbData = requires(T t, StringSlice s, DbValues &v) { t(s, v); };
template<DbData T>
bool db_exec(const char *sql, DbArgs args, T &data) {
return db_exec(sql, std::move(args), (db_exec_callback) std::ref(data));
}

View File

@ -1,21 +1,24 @@
#![feature(format_args_nl)]
#![feature(try_blocks)]
#![feature(let_chains)]
#![feature(fn_traits)]
#![allow(clippy::missing_safety_doc)]
use base::Utf8CStr;
use cert::read_certificate;
use daemon::{daemon_entry, find_apk_path, get_magiskd, MagiskD};
use db::get_default_db_settings;
use logging::{
android_logging, magisk_logging, zygisk_close_logd, zygisk_get_logd, zygisk_logging,
};
use mount::{find_preinit_device, revert_unmount, setup_mounts, clean_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};
mod cert;
#[path = "../include/consts.rs"]
mod consts;
mod daemon;
mod db;
mod logging;
mod mount;
mod resetprop;
@ -76,6 +79,67 @@ pub mod ffi {
fn switch_mnt_ns(pid: i32) -> i32;
}
enum DbEntryKey {
RootAccess,
SuMultiuserMode,
SuMntNs,
DenylistConfig,
ZygiskConfig,
BootloopCount,
SuManager,
}
#[repr(i32)]
enum RootAccess {
Disabled,
AppsOnly,
AdbOnly,
AppsAndAdb,
}
#[repr(i32)]
enum MultiuserMode {
OwnerOnly,
OwnerManaged,
User,
}
#[repr(i32)]
enum MntNsMode {
Global,
Requester,
Isolate,
}
#[derive(Default)]
struct DbSettings {
root_access: RootAccess,
multiuser_mode: MultiuserMode,
mnt_ns: MntNsMode,
boot_count: i32,
denylist: bool,
zygisk: bool,
}
unsafe extern "C++" {
include!("include/sqlite.hpp");
fn sqlite3_errstr(code: i32) -> *const c_char;
type sqlite3;
fn open_and_init_db() -> *mut sqlite3;
type DbValues;
type DbStatement;
fn get_int(self: &DbValues, index: i32) -> i32;
#[cxx_name = "get_str"]
fn get_text(self: &DbValues, index: i32) -> &str;
fn bind_text(self: Pin<&mut DbStatement>, index: i32, val: &str) -> i32;
fn bind_int64(self: Pin<&mut DbStatement>, index: i32, val: i64) -> i32;
}
extern "Rust" {
fn rust_test_entry();
fn android_logging();
@ -102,10 +166,22 @@ pub mod ffi {
extern "Rust" {
type MagiskD;
fn setup_logfile(self: &MagiskD);
fn is_emulator(self: &MagiskD) -> bool;
fn is_recovery(self: &MagiskD) -> bool;
fn boot_stage_handler(self: &MagiskD, client: i32, code: i32);
#[cxx_name = "get_db_settings"]
fn get_db_settings_for_cxx(self: &MagiskD, cfg: &mut DbSettings) -> bool;
fn get_db_setting(&self, key: DbEntryKey) -> i32;
#[cxx_name = "set_db_setting"]
fn set_db_setting_for_cxx(&self, key: DbEntryKey, value: i32) -> bool;
fn get_db_string(&self, key: DbEntryKey) -> String;
#[cxx_name = "rm_db_string"]
fn rm_db_string_for_cxx(&self, key: DbEntryKey) -> bool;
#[cxx_name = "db_exec"]
fn db_exec_for_cxx(&self, client_fd: i32);
#[cxx_name = "DbSettings"]
fn get_default_db_settings() -> DbSettings;
#[cxx_name = "MagiskD"]
fn get_magiskd() -> &'static MagiskD;
}

View File

@ -1,7 +1,7 @@
#include <base.hpp>
#include <consts.hpp>
#include <core.hpp>
#include <db.hpp>
#include <sqlite.hpp>
#include <flags.h>
using namespace std;
@ -195,9 +195,7 @@ static int get_pkg_uid(int user, const char *pkg) {
int get_manager(int user, string *pkg, bool install) {
mutex_guard g(pkg_lock);
db_strings str;
get_db_strings(str, SU_MANAGER);
string db_pkg(std::move(str.su_manager));
string db_pkg = (string) MagiskD().get_db_string(DbEntryKey::SuManager);
// If database changed, always re-check files
if (db_pkg != repackaged_pkg) {
@ -251,7 +249,7 @@ int get_manager(int user, string *pkg, bool install) {
case CERT_MISMATCH:
install = true;
case NOT_INSTALLED:
rm_db_strings(SU_MANAGER);
MagiskD().rm_db_string(DbEntryKey::SuManager);
break;
}
}

View File

@ -1,16 +1,22 @@
#include <dlfcn.h>
#include <consts.hpp>
#include <base.hpp>
#include <sqlite.hpp>
using namespace std;
#define DB_VERSION 12
#define DB_VERSION_STR "12"
#define DBLOGV(...)
//#define DBLOGV(...) LOGD("magiskdb: " __VA_ARGS__)
// SQLite APIs
int (*sqlite3_open_v2)(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);
int (*sqlite3_close)(sqlite3 *db);
static int (*sqlite3_open_v2)(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs);
static int (*sqlite3_close)(sqlite3 *db);
const char *(*sqlite3_errstr)(int);
static int (*sqlite3_prepare_v2)(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail);
static int (*sqlite3_bind_parameter_count)(sqlite3_stmt*);
static int (*sqlite3_bind_int64)(sqlite3_stmt*, int, int64_t);
@ -44,7 +50,7 @@ constexpr char apex_path[] = "/apex/com.android.runtime/lib64:/apex/com.android.
constexpr char apex_path[] = "/apex/com.android.runtime/lib:/apex/com.android.art/lib:/apex/com.android.i18n/lib:";
#endif
bool load_sqlite() {
static bool load_sqlite() {
static int dl_init = 0;
if (dl_init)
return dl_init > 0;
@ -96,7 +102,11 @@ using sql_exec_callback_real = void(*)(void*, StringSlice, sqlite3_stmt*);
#define sql_chk(fn, ...) if (int rc = fn(__VA_ARGS__); rc != SQLITE_OK) return rc
int sql_exec(sqlite3 *db, rust::Str zSql, sql_bind_callback bind_cb, void *bind_cookie, sql_exec_callback exec_cb, void *exec_cookie) {
// Exports to Rust
extern "C" int sql_exec_impl(
sqlite3 *db, rust::Str zSql,
sql_bind_callback bind_cb = nullptr, void *bind_cookie = nullptr,
sql_exec_callback exec_cb = nullptr, void *exec_cookie = nullptr) {
const char *sql = zSql.begin();
unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)> stmt(nullptr, sqlite3_finalize);
@ -142,12 +152,12 @@ int sql_exec(sqlite3 *db, rust::Str zSql, sql_bind_callback bind_cb, void *bind_
return SQLITE_OK;
}
int DbValues::get_int(int index) {
return sqlite3_column_int(reinterpret_cast<sqlite3_stmt*>(this), index);
int DbValues::get_int(int index) const {
return sqlite3_column_int((sqlite3_stmt*) this, index);
}
const char *DbValues::get_text(int index) {
return sqlite3_column_text(reinterpret_cast<sqlite3_stmt*>(this), index);
const char *DbValues::get_text(int index) const {
return sqlite3_column_text((sqlite3_stmt*) this, index);
}
int DbStatement::bind_int64(int index, int64_t val) {
@ -157,3 +167,186 @@ int DbStatement::bind_int64(int index, int64_t val) {
int DbStatement::bind_text(int index, rust::Str val) {
return sqlite3_bind_text(reinterpret_cast<sqlite3_stmt*>(this), index, val.data(), val.size(), nullptr);
}
#define sql_chk_log(fn, ...) if (int rc = fn(__VA_ARGS__); rc != SQLITE_OK) { \
LOGE("sqlite3(db.cpp:%d): %s\n", __LINE__, sqlite3_errstr(rc)); \
return false; \
}
static bool open_and_init_db_impl(sqlite3 **dbOut) {
if (!load_sqlite()) {
LOGE("sqlite3: Cannot load libsqlite.so\n");
return false;
}
unique_ptr<sqlite3, decltype(sqlite3_close)> db(nullptr, sqlite3_close);
{
sqlite3 *sql;
sql_chk_log(sqlite3_open_v2, MAGISKDB, &sql,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr);
db.reset(sql);
}
int ver = 0;
bool upgrade = false;
auto ver_cb = [](void *ver, auto, DbValues &data) {
*static_cast<int *>(ver) = data.get_int(0);
};
sql_chk_log(sql_exec_impl, db.get(), "PRAGMA user_version", nullptr, nullptr, ver_cb, &ver);
if (ver > DB_VERSION) {
// Don't support downgrading database
LOGE("sqlite3: Downgrading database is not supported\n");
return false;
}
auto create_policy = [&] {
return sql_exec_impl(db.get(),
"CREATE TABLE IF NOT EXISTS policies "
"(uid INT, policy INT, until INT, logging INT, "
"notification INT, PRIMARY KEY(uid))");
};
auto create_settings = [&] {
return sql_exec_impl(db.get(),
"CREATE TABLE IF NOT EXISTS settings "
"(key TEXT, value INT, PRIMARY KEY(key))");
};
auto create_strings = [&] {
return sql_exec_impl(db.get(),
"CREATE TABLE IF NOT EXISTS strings "
"(key TEXT, value TEXT, PRIMARY KEY(key))");
};
auto create_denylist = [&] {
return sql_exec_impl(db.get(),
"CREATE TABLE IF NOT EXISTS denylist "
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process))");
};
// Database changelog:
//
// 0 - 6: DB stored in app private data. There are no longer any code in the project to
// migrate these data, so no need to take any of these versions into consideration.
// 7 : create table `hidelist` (process TEXT, PRIMARY KEY(process))
// 8 : add new column (package_name TEXT) to table `hidelist`
// 9 : rebuild table `hidelist` to change primary key (PRIMARY KEY(package_name, process))
// 10: remove table `logs`
// 11: remove table `hidelist` and create table `denylist` (same data structure)
// 12: rebuild table `policies` to drop column `package_name`
if (/* 0, 1, 2, 3, 4, 5, 6 */ ver <= 6) {
sql_chk_log(create_policy);
sql_chk_log(create_settings);
sql_chk_log(create_strings);
sql_chk_log(create_denylist);
// Directly jump to latest
ver = DB_VERSION;
upgrade = true;
}
if (ver == 7) {
sql_chk_log(sql_exec_impl, db.get(),
"BEGIN TRANSACTION;"
"ALTER TABLE hidelist RENAME TO hidelist_tmp;"
"CREATE TABLE IF NOT EXISTS hidelist "
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));"
"INSERT INTO hidelist SELECT process as package_name, process FROM hidelist_tmp;"
"DROP TABLE hidelist_tmp;"
"COMMIT;");
// Directly jump to version 9
ver = 9;
upgrade = true;
}
if (ver == 8) {
sql_chk_log(sql_exec_impl, db.get(),
"BEGIN TRANSACTION;"
"ALTER TABLE hidelist RENAME TO hidelist_tmp;"
"CREATE TABLE IF NOT EXISTS hidelist "
"(package_name TEXT, process TEXT, PRIMARY KEY(package_name, process));"
"INSERT INTO hidelist SELECT * FROM hidelist_tmp;"
"DROP TABLE hidelist_tmp;"
"COMMIT;");
ver = 9;
upgrade = true;
}
if (ver == 9) {
sql_chk_log(sql_exec_impl, db.get(), "DROP TABLE IF EXISTS logs", nullptr, nullptr);
ver = 10;
upgrade = true;
}
if (ver == 10) {
sql_chk_log(sql_exec_impl, db.get(),
"DROP TABLE IF EXISTS hidelist;"
"DELETE FROM settings WHERE key='magiskhide';");
sql_chk_log(create_denylist);
ver = 11;
upgrade = true;
}
if (ver == 11) {
sql_chk_log(sql_exec_impl, db.get(),
"BEGIN TRANSACTION;"
"ALTER TABLE policies RENAME TO policies_tmp;"
"CREATE TABLE IF NOT EXISTS policies "
"(uid INT, policy INT, until INT, logging INT, "
"notification INT, PRIMARY KEY(uid));"
"INSERT INTO policies "
"SELECT uid, policy, until, logging, notification FROM policies_tmp;"
"DROP TABLE policies_tmp;"
"COMMIT;");
ver = 12;
upgrade = true;
}
if (upgrade) {
// Set version
sql_chk_log(sql_exec_impl, db.get(), "PRAGMA user_version=" DB_VERSION_STR);
}
*dbOut = db.release();
return true;
}
sqlite3 *open_and_init_db() {
sqlite3 *db = nullptr;
return open_and_init_db_impl(&db) ? db : nullptr;
}
// Exported from Rust
extern "C" int sql_exec_rs(
rust::Str zSql,
sql_bind_callback bind_cb, void *bind_cookie,
sql_exec_callback exec_cb, void *exec_cookie);
bool db_exec(const char *sql, DbArgs args, db_exec_callback exec_fn) {
using db_bind_callback = std::function<int(int, DbStatement&)>;
db_bind_callback bind_fn = {};
sql_bind_callback bind_cb = nullptr;
if (!args.empty()) {
bind_fn = std::ref(args);
bind_cb = [](void *v, int index, DbStatement &stmt) -> int {
auto fn = static_cast<db_bind_callback*>(v);
return fn->operator()(index, stmt);
};
}
sql_exec_callback exec_cb = nullptr;
if (exec_fn) {
exec_cb = [](void *v, StringSlice columns, DbValues &data) {
auto fn = static_cast<db_exec_callback*>(v);
fn->operator()(columns, data);
};
}
sql_chk_log(sql_exec_rs, sql, bind_cb, &bind_fn, exec_cb, &exec_fn);
return true;
}
int DbArgs::operator()(int index, DbStatement &stmt) {
if (curr < args.size()) {
const auto &arg = args[curr++];
switch (arg.type) {
case DbArg::INT:
return stmt.bind_int64(index, arg.int_val);
case DbArg::TEXT:
return stmt.bind_text(index, arg.str_val);
}
}
return SQLITE_OK;
}

View File

@ -4,7 +4,7 @@
#include <sys/stat.h>
#include <memory>
#include <db.hpp>
#include <sqlite.hpp>
#include <core.hpp>
#define DEFAULT_SHELL "/system/bin/sh"
@ -47,7 +47,7 @@ public:
// These should be guarded with internal lock
int eval_uid; // The effective UID, taking multiuser settings into consideration
db_settings cfg;
struct DbSettings cfg;
su_access access;
std::string mgr_pkg;
int mgr_uid;

View File

@ -18,7 +18,8 @@ static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
static shared_ptr<su_info> cached;
su_info::su_info(int uid) :
uid(uid), eval_uid(-1), mgr_uid(-1), timestamp(0), _lock(PTHREAD_MUTEX_INITIALIZER) {}
uid(uid), eval_uid(-1), cfg(DbSettings()),
mgr_uid(-1), timestamp(0), _lock(PTHREAD_MUTEX_INITIALIZER) {}
su_info::~su_info() {
pthread_mutex_destroy(&_lock);
@ -57,20 +58,20 @@ void su_access::operator()(StringSlice columns, DbValues &data) {
void su_info::check_db() {
eval_uid = uid;
get_db_settings(cfg);
MagiskD().get_db_settings(cfg);
// Check multiuser settings
switch (cfg.multiuser_mode) {
case MULTIUSER_MODE_OWNER_ONLY:
case MultiuserMode::OwnerOnly:
if (to_user_id(uid) != 0) {
eval_uid = -1;
access.silent_deny();
}
break;
case MULTIUSER_MODE_OWNER_MANAGED:
case MultiuserMode::OwnerManaged:
eval_uid = to_app_id(uid);
break;
case MULTIUSER_MODE_USER:
case MultiuserMode::User:
default:
break;
}
@ -93,35 +94,35 @@ bool uid_granted_root(int uid) {
if (uid == AID_ROOT)
return true;
db_settings cfg;
get_db_settings(cfg);
auto cfg = DbSettings();
MagiskD().get_db_settings(cfg);
// Check user root access settings
switch (cfg.root_access) {
case ROOT_ACCESS_DISABLED:
case RootAccess::Disabled:
return false;
case ROOT_ACCESS_APPS_ONLY:
case RootAccess::AppsOnly:
if (uid == AID_SHELL)
return false;
break;
case ROOT_ACCESS_ADB_ONLY:
case RootAccess::AdbOnly:
if (uid != AID_SHELL)
return false;
break;
case ROOT_ACCESS_APPS_AND_ADB:
case RootAccess::AppsAndAdb:
break;
}
// Check multiuser settings
switch (cfg.multiuser_mode) {
case MULTIUSER_MODE_OWNER_ONLY:
case MultiuserMode::OwnerOnly:
if (to_user_id(uid) != 0)
return false;
break;
case MULTIUSER_MODE_OWNER_MANAGED:
case MultiuserMode::OwnerManaged:
uid = to_app_id(uid);
break;
case MULTIUSER_MODE_USER:
case MultiuserMode::User:
default:
break;
}
@ -193,23 +194,23 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
// Check su access settings
switch (info->cfg.root_access) {
case ROOT_ACCESS_DISABLED:
case RootAccess::Disabled:
LOGW("Root access is disabled!\n");
info->access.silent_deny();
break;
case ROOT_ACCESS_ADB_ONLY:
case RootAccess::AdbOnly:
if (info->uid != AID_SHELL) {
LOGW("Root access limited to ADB only!\n");
info->access.silent_deny();
}
break;
case ROOT_ACCESS_APPS_ONLY:
case RootAccess::AppsOnly:
if (info->uid == AID_SHELL) {
LOGW("Root access is disabled for ADB!\n");
info->access.silent_deny();
}
break;
case ROOT_ACCESS_APPS_AND_ADB:
case RootAccess::AppsAndAdb:
default:
break;
}
@ -395,19 +396,19 @@ void su_daemon_handler(int client, const sock_cred *cred) {
if (ctx.req.target == -1)
ctx.req.target = ctx.pid;
else if (ctx.req.target == 0)
ctx.info->cfg.mnt_ns = NAMESPACE_MODE_GLOBAL;
else if (ctx.info->cfg.mnt_ns == NAMESPACE_MODE_GLOBAL)
ctx.info->cfg.mnt_ns = NAMESPACE_MODE_REQUESTER;
ctx.info->cfg.mnt_ns = MntNsMode::Global;
else if (ctx.info->cfg.mnt_ns == MntNsMode::Global)
ctx.info->cfg.mnt_ns = MntNsMode::Requester;
switch (ctx.info->cfg.mnt_ns) {
case NAMESPACE_MODE_GLOBAL:
case MntNsMode::Global:
LOGD("su: use global namespace\n");
break;
case NAMESPACE_MODE_REQUESTER:
case MntNsMode::Requester:
LOGD("su: use namespace of pid=[%d]\n", ctx.req.target);
if (switch_mnt_ns(ctx.req.target))
LOGD("su: setns failed, fallback to global\n");
break;
case NAMESPACE_MODE_ISOLATE:
case MntNsMode::Isolate:
LOGD("su: use new isolated namespace\n");
switch_mnt_ns(ctx.req.target);
xunshare(CLONE_NEWNS);