Rewrite get_manager

This commit is contained in:
vvb2060 2024-02-01 04:33:35 +08:00 committed by John Wu
parent 6aab856de7
commit 2963d4ca9e
6 changed files with 171 additions and 208 deletions

View File

@ -7,6 +7,7 @@ import android.content.Intent
import android.os.Build import android.os.Build
import android.widget.Toast import android.widget.Toast
import com.topjohnwu.magisk.StubApk import com.topjohnwu.magisk.StubApk
import com.topjohnwu.magisk.core.AppApkPath
import com.topjohnwu.magisk.core.BuildConfig.APP_PACKAGE_NAME import com.topjohnwu.magisk.core.BuildConfig.APP_PACKAGE_NAME
import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Const
@ -193,7 +194,7 @@ object AppMigration {
} }
Config.suManager = pkg Config.suManager = pkg
val cmd = "adb_pm_install $repack $pkg" val cmd = "touch $AppApkPath; adb_pm_install $repack $pkg"
if (Shell.cmd(cmd).exec().isSuccess) return true if (Shell.cmd(cmd).exec().isSuccess) return true
try { try {
@ -242,7 +243,7 @@ object AppMigration {
dialog.dismiss() dialog.dismiss()
} }
Config.suManager = "" Config.suManager = ""
val cmd = "adb_pm_install $apk $APP_PACKAGE_NAME" val cmd = "touch $AppApkPath; adb_pm_install $apk $APP_PACKAGE_NAME"
if (Shell.cmd(cmd).await().isSuccess) return if (Shell.cmd(cmd).await().isSuccess) return
val success = withContext(Dispatchers.IO) { val success = withContext(Dispatchers.IO) {
try { try {

View File

@ -204,7 +204,6 @@ void MagiskD::boot_complete() const {
xmkdir(SECURE_DIR, 0700); xmkdir(SECURE_DIR, 0700);
// Ensure manager exists // Ensure manager exists
check_pkg_refresh();
get_manager(0, nullptr, true); get_manager(0, nullptr, true);
reset_zygisk(true); reset_zygisk(true);

View File

@ -62,11 +62,8 @@ void zygisk_handler(int client, const sock_cred *cred);
// Package // Package
void preserve_stub_apk(); void preserve_stub_apk();
void check_pkg_refresh();
std::vector<bool> get_app_no_list(); std::vector<bool> get_app_no_list();
// Call check_pkg_refresh() before calling get_manager(...) int get_manager(int user, std::string *pkg = nullptr, bool install = false);
// to make sure the package state is invalidated!
int get_manager(int user_id = 0, std::string *pkg = nullptr, bool install = false);
void prune_su_access(); void prune_su_access();
// Module stuffs // Module stuffs

View File

@ -15,31 +15,22 @@ using rust::Vec;
static pthread_mutex_t pkg_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t pkg_lock = PTHREAD_MUTEX_INITIALIZER;
// pkg_lock protects all following variables // pkg_lock protects all following variables
static int mgr_app_id = -1;
static bool skip_mgr_check;
static timespec *app_ts;
static string *mgr_pkg;
static Vec<uint8_t> *mgr_cert;
static int stub_apk_fd = -1; static int stub_apk_fd = -1;
static const Vec<uint8_t> *default_cert; static int repackaged_app_id = -1; // Only used by dyn
static string repackaged_pkg;
static Vec<uint8_t> repackaged_cert;
static Vec<uint8_t> trusted_cert;
static map<int, pair<string, time_t>> user_to_check;
enum status {
INSTALLED,
NO_INSTALLED,
CERT_MISMATCH,
};
static bool operator==(const Vec<uint8_t> &a, const Vec<uint8_t> &b) { static bool operator==(const Vec<uint8_t> &a, const Vec<uint8_t> &b) {
return a.size() == b.size() && memcmp(a.data(), b.data(), a.size()) == 0; return a.size() == b.size() && memcmp(a.data(), b.data(), a.size()) == 0;
} }
void check_pkg_refresh() {
mutex_guard g(pkg_lock);
if (app_ts == nullptr)
default_new(app_ts);
if (struct stat st{}; stat("/data/app", &st) == 0) {
if (memcmp(app_ts, &st.st_mtim, sizeof(timespec)) == 0) {
return;
}
memcpy(app_ts, &st.st_mtim, sizeof(timespec));
}
skip_mgr_check = false;
}
// app_id = app_no + AID_APP_START // app_id = app_no + AID_APP_START
// app_no range: [0, 9999] // app_no range: [0, 9999]
vector<bool> get_app_no_list() { vector<bool> get_app_no_list() {
@ -77,9 +68,7 @@ void preserve_stub_apk() {
string stub_path = get_magisk_tmp() + "/stub.apk"s; string stub_path = get_magisk_tmp() + "/stub.apk"s;
stub_apk_fd = xopen(stub_path.data(), O_RDONLY | O_CLOEXEC); stub_apk_fd = xopen(stub_path.data(), O_RDONLY | O_CLOEXEC);
unlink(stub_path.data()); unlink(stub_path.data());
auto cert = read_certificate(stub_apk_fd, -1); trusted_cert = read_certificate(stub_apk_fd, MAGISK_VER_CODE);
if (!cert.empty())
default_cert = new Vec(std::move(cert));
lseek(stub_apk_fd, 0, SEEK_SET); lseek(stub_apk_fd, 0, SEEK_SET);
} }
@ -96,194 +85,174 @@ static void install_stub() {
install_apk(apk); install_apk(apk);
} }
int get_manager(int user_id, string *pkg, bool install) { static status check_dyn(int user, string &pkg) {
struct stat st{};
char apk[PATH_MAX];
ssprintf(apk, sizeof(apk),
"%s/%d/%s/dyn/current.apk", APP_DATA_DIR, user, pkg.data());
int dyn = open(apk, O_RDONLY | O_CLOEXEC);
if (dyn < 0) {
LOGW("pkg: no dyn APK, ignore\n");
return NO_INSTALLED;
}
auto cert = read_certificate(dyn, MAGISK_VER_CODE);
fstat(dyn, &st);
close(dyn);
if (cert.empty() || cert != trusted_cert) {
LOGE("pkg: dyn APK signature mismatch: %s\n", apk);
#if ENFORCE_SIGNATURE
clear_pkg(pkg.data(), user);
return CERT_MISMATCH;
#endif
}
repackaged_app_id = to_app_id(st.st_uid);
user_to_check[user] = make_pair(apk, st.st_ctim.tv_sec);
return INSTALLED;
}
static status check_stub(int user, string &pkg) {
struct stat st{};
byte_array<PATH_MAX> buf;
find_apk_path(pkg, buf);
string apk((const char *) buf.buf(), buf.sz());
int fd = xopen(apk.data(), O_RDONLY | O_CLOEXEC);
if (fd < 0)
return NO_INSTALLED;
auto cert = read_certificate(fd, -1);
fstat(fd, &st);
close(fd);
if (cert.empty() || (pkg == repackaged_pkg && cert != repackaged_cert)) {
LOGE("pkg: repackaged APK signature invalid: %s\n", apk.data());
uninstall_pkg(pkg.data());
return CERT_MISMATCH;
}
repackaged_pkg.swap(pkg);
repackaged_cert.swap(cert);
user_to_check[user] = make_pair(apk, st.st_ctim.tv_sec);
return INSTALLED;
}
static status check_orig(int user) {
struct stat st{};
byte_array<PATH_MAX> buf;
find_apk_path(JAVA_PACKAGE_NAME, buf);
string apk((const char *) buf.buf(), buf.sz());
int fd = xopen(apk.data(), O_RDONLY | O_CLOEXEC);
if (fd < 0)
return NO_INSTALLED;
auto cert = read_certificate(fd, MAGISK_VER_CODE);
fstat(fd, &st);
close(fd);
if (cert.empty() || cert != trusted_cert) {
LOGE("pkg: APK signature mismatch: %s\n", apk.data());
#if ENFORCE_SIGNATURE
uninstall_pkg(JAVA_PACKAGE_NAME);
return CERT_MISMATCH;
#endif
}
user_to_check[user] = make_pair(apk, st.st_ctim.tv_sec);
return INSTALLED;
}
static int get_pkg_uid(int user, const char *pkg) {
char path[PATH_MAX];
struct stat st{};
ssprintf(path, sizeof(path), "%s/%d/%s", APP_DATA_DIR, user, pkg);
if (stat(path, &st) == 0) {
return st.st_uid;
}
return -1;
}
int get_manager(int user, string *pkg, bool install) {
mutex_guard g(pkg_lock); mutex_guard g(pkg_lock);
char app_path[128];
struct stat st{}; struct stat st{};
if (mgr_pkg == nullptr) const auto &[path, time] = user_to_check[user];
default_new(mgr_pkg); if (stat(path.data(), &st) == 0 && st.st_ctim.tv_sec == time) {
if (mgr_cert == nullptr) // no APK
default_new(mgr_cert); if (path == "/data/system/packages.xml") {
if (install) install_stub();
auto check_dyn = [&](int u) -> bool { if (pkg) pkg->clear();
#if ENFORCE_SIGNATURE return -1;
ssprintf(app_path, sizeof(app_path),
"%s/%d/%s/dyn/current.apk", APP_DATA_DIR, u, mgr_pkg->data());
int dyn = open(app_path, O_RDONLY | O_CLOEXEC);
if (dyn < 0) {
LOGW("pkg: no dyn APK, ignore\n");
return false;
} }
auto cert = read_certificate(dyn, MAGISK_VER_CODE); // dyn APK is still the same
bool mismatch = default_cert && cert != *default_cert; if (path.starts_with(APP_DATA_DIR)) {
close(dyn); if (pkg) *pkg = repackaged_pkg;
if (mismatch) { return user * AID_USER_OFFSET + repackaged_app_id;
LOGE("pkg: dyn APK signature mismatch: %s\n", app_path);
clear_pkg(mgr_pkg->data(), u);
return false;
} }
#endif // stub APK is still the same
return true; if (!repackaged_pkg.empty()) {
}; if (check_dyn(user, repackaged_pkg) == INSTALLED) {
if (pkg) *pkg = repackaged_pkg;
if (skip_mgr_check) { return user * AID_USER_OFFSET + repackaged_app_id;
if (mgr_app_id >= 0) {
// Just need to check whether the app is installed in the user
const char *name = mgr_pkg->empty() ? JAVA_PACKAGE_NAME : mgr_pkg->data();
ssprintf(app_path, sizeof(app_path), "%s/%d/%s", APP_DATA_DIR, user_id, name);
if (access(app_path, F_OK) == 0) {
// Always check dyn signature for repackaged app
if (!mgr_pkg->empty() && !check_dyn(user_id))
goto ignore;
if (pkg) *pkg = name;
return user_id * AID_USER_OFFSET + mgr_app_id;
} else { } else {
goto not_found; if (pkg) pkg->clear();
return -1;
} }
} }
} else { // orig APK is still the same
// Here, we want to actually find the manager app and cache the results. int uid = get_pkg_uid(user, JAVA_PACKAGE_NAME);
// This means that we check all users, not just the requested user. if (uid < 0) {
// Certificates are also verified to prevent manipulation. if (pkg) pkg->clear();
return -1;
skip_mgr_check = true; } else {
db_strings str;
get_db_strings(str, SU_MANAGER);
vector<int> users;
bool collected = false;
auto collect_users = [&] {
if (collected)
return;
collected = true;
auto data_dir = xopen_dir(APP_DATA_DIR);
if (!data_dir)
return;
dirent *entry;
while ((entry = xreaddir(data_dir.get()))) {
// Only collect users not requested as we've already checked it
if (int u = parse_int(entry->d_name); u >= 0 && u != user_id)
users.push_back(parse_int(entry->d_name));
}
};
if (!str[SU_MANAGER].empty()) {
// Check the repackaged package name
bool invalid = false;
auto check_stub_apk = [&](int u) -> bool {
ssprintf(app_path, sizeof(app_path),
"%s/%d/%s", APP_DATA_DIR, u, str[SU_MANAGER].data());
if (stat(app_path, &st) == 0) {
int app_id = to_app_id(st.st_uid);
byte_array<PATH_MAX> apk;
find_apk_path(str[SU_MANAGER], apk);
int fd = xopen((const char *) apk.buf(), O_RDONLY | O_CLOEXEC);
auto cert = read_certificate(fd, -1);
close(fd);
// Verify validity
if (str[SU_MANAGER] == *mgr_pkg) {
if (app_id != mgr_app_id || cert.empty() || cert != *mgr_cert) {
// app ID or cert should never change
LOGE("pkg: repackaged APK signature invalid: %s\n", apk.buf());
uninstall_pkg(mgr_pkg->data());
invalid = true;
install = true;
return false;
}
}
mgr_pkg->swap(str[SU_MANAGER]);
mgr_app_id = app_id;
mgr_cert->swap(cert);
return true;
}
return false;
};
if (check_stub_apk(user_id)) {
if (!check_dyn(user_id))
goto ignore;
if (pkg) *pkg = *mgr_pkg;
return st.st_uid;
}
if (!invalid) {
collect_users();
for (int u : users) {
if (check_stub_apk(u)) {
// Found repackaged app, but not installed in the requested user
goto not_found;
}
if (invalid)
break;
}
}
// Repackaged app not found, fall through
}
// Check the original package name
bool invalid = false;
auto check_apk = [&](int u) -> bool {
ssprintf(app_path, sizeof(app_path), "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, u);
if (stat(app_path, &st) == 0) {
#if ENFORCE_SIGNATURE
byte_array<PATH_MAX> apk;
find_apk_path(JAVA_PACKAGE_NAME, apk);
int fd = xopen((const char *) apk.buf(), O_RDONLY | O_CLOEXEC);
auto cert = read_certificate(fd, MAGISK_VER_CODE);
close(fd);
if (default_cert && cert != *default_cert) {
// Found APK with invalid signature, force replace with stub
LOGE("pkg: APK signature mismatch: %s\n", apk.buf());
uninstall_pkg(JAVA_PACKAGE_NAME);
invalid = true;
install = true;
return false;
}
#endif
mgr_pkg->clear();
mgr_cert->clear();
mgr_app_id = to_app_id(st.st_uid);
return true;
}
return false;
};
if (check_apk(user_id)) {
if (pkg) *pkg = JAVA_PACKAGE_NAME; if (pkg) *pkg = JAVA_PACKAGE_NAME;
return st.st_uid; return uid;
}
if (!invalid) {
collect_users();
for (int u : users) {
if (check_apk(u)) {
// Found app, but not installed in the requested user
goto not_found;
}
if (invalid)
break;
}
} }
} }
// No manager app is found, clear all cached value db_strings str;
mgr_app_id = -1; get_db_strings(str, SU_MANAGER);
mgr_pkg->clear();
mgr_cert->clear();
if (install)
install_stub();
not_found: if (!str[SU_MANAGER].empty()) {
LOGW("pkg: cannot find %s for user=[%d]\n", switch (check_stub(user, str[SU_MANAGER])) {
mgr_pkg->empty() ? JAVA_PACKAGE_NAME : mgr_pkg->data(), user_id); case INSTALLED:
ignore: if (check_dyn(user, repackaged_pkg) == INSTALLED) {
if (pkg) *pkg = repackaged_pkg;
return user * AID_USER_OFFSET + repackaged_app_id;
} else {
if (pkg) pkg->clear();
return -1;
}
case CERT_MISMATCH:
install = true;
case NO_INSTALLED:
break;
}
}
repackaged_pkg.clear();
repackaged_cert.clear();
switch (check_orig(user)) {
case INSTALLED: {
int uid = get_pkg_uid(user, JAVA_PACKAGE_NAME);
if (uid < 0) {
if (pkg) pkg->clear();
return -1;
} else {
if (pkg) *pkg = JAVA_PACKAGE_NAME;
return uid;
}
}
case CERT_MISMATCH:
install = true;
case NO_INSTALLED:
break;
}
auto xml = "/data/system/packages.xml";
stat(xml, &st);
user_to_check[user] = make_pair(xml, st.st_ctim.tv_sec);
if (install) install_stub();
if (pkg) pkg->clear(); if (pkg) pkg->clear();
return -1; return -1;
} }

View File

@ -80,7 +80,6 @@ void su_info::check_db() {
// We need to check our manager // We need to check our manager
if (access.log || access.notify) { if (access.log || access.notify) {
check_pkg_refresh();
mgr_uid = get_manager(to_user_id(eval_uid), &mgr_pkg, true); mgr_uid = get_manager(to_user_id(eval_uid), &mgr_pkg, true);
} }
} }

View File

@ -128,12 +128,10 @@ static void get_process_info(int client, const sock_cred *cred) {
uint32_t flags = 0; uint32_t flags = 0;
check_pkg_refresh();
if (is_deny_target(uid, process)) { if (is_deny_target(uid, process)) {
flags |= PROCESS_ON_DENYLIST; flags |= PROCESS_ON_DENYLIST;
} }
int manager_app_id = get_manager(); if (get_manager(to_user_id(uid)) == uid) {
if (to_app_id(uid) == manager_app_id) {
flags |= PROCESS_IS_MAGISK_APP; flags |= PROCESS_IS_MAGISK_APP;
} }
if (denylist_enforced) { if (denylist_enforced) {