KernelSU/kernel/allowlist.c
Wang Han eeffecbd1b
Some checks failed
Build Manager / build-lkm (push) Waiting to run
Build Manager / build-ksud (macos-latest, aarch64-apple-darwin) (push) Blocked by required conditions
Build Manager / build-ksud (macos-latest, x86_64-apple-darwin) (push) Blocked by required conditions
Build Manager / build-ksud (ubuntu-latest, aarch64-linux-android) (push) Blocked by required conditions
Build Manager / build-ksud (ubuntu-latest, aarch64-unknown-linux-musl) (push) Blocked by required conditions
Build Manager / build-ksud (ubuntu-latest, x86_64-linux-android) (push) Blocked by required conditions
Build Manager / build-ksud (ubuntu-latest, x86_64-pc-windows-gnu) (push) Blocked by required conditions
Build Manager / build-ksud (ubuntu-latest, x86_64-unknown-linux-musl) (push) Blocked by required conditions
Build Manager / build-manager (push) Blocked by required conditions
Build Kernel - Android 12 / build-kernel (2024-05, 209) (push) Has been cancelled
Build Kernel - Android 12 / build-kernel (2024-08, 218) (push) Has been cancelled
Build Kernel - Android 12 / build-kernel (2024-11, 226) (push) Has been cancelled
Build Kernel - Android 12 / check-build-kernel (push) Has been cancelled
Build Kernel - Android 13 / build-kernel (2024-05, 148, 5.15) (push) Has been cancelled
Build Kernel - Android 13 / build-kernel (2024-05, 209, 5.10) (push) Has been cancelled
Build Kernel - Android 13 / build-kernel (2024-06, 210, 5.10) (push) Has been cancelled
Build Kernel - Android 13 / build-kernel (2024-07, 149, 5.15) (push) Has been cancelled
Build Kernel - Android 13 / build-kernel (2024-07, 214, 5.10) (push) Has been cancelled
Build Kernel - Android 13 / build-kernel (2024-08, 151, 5.15) (push) Has been cancelled
Build Kernel - Android 13 / build-kernel (2024-08, 218, 5.10) (push) Has been cancelled
Build Kernel - Android 13 / build-kernel (2024-09, 153, 5.15) (push) Has been cancelled
Build Kernel - Android 13 / build-kernel (2024-11, 167, 5.15) (push) Has been cancelled
Build Kernel - Android 13 / build-kernel (2024-11, 223, 5.10) (push) Has been cancelled
Build Kernel - Android 13 / check-build-kernel (2024-11, 167, 5.15) (push) Has been cancelled
Build Kernel - Android 13 / check-build-kernel (2024-11, 223, 5.10) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-05, 148, 5.15) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-05, 75, 6.1) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-06, 149, 5.15) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-06, 78, 6.1) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-07, 153, 5.15) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-07, 84, 6.1) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-08, 158, 5.15) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-08, 90, 6.1) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-09, 164, 5.15) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-09, 93, 6.1) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-10, 99, 6.1) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-11, 112, 6.1) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-11, 167, 5.15) (push) Has been cancelled
Build Kernel - Android 14 / build-kernel (2024-12, 115, 6.1) (push) Has been cancelled
Build Kernel - Android 14 / check-build-kernel (2024-11, 167, 5.15) (push) Has been cancelled
Build Kernel - Android 14 / check-build-kernel (2024-12, 115, 6.1) (push) Has been cancelled
Build Kernel - Android 15 / build-kernel (2024-08, 30, 6.6) (push) Has been cancelled
Build Kernel - Android 15 / build-kernel (2024-09, 46, 6.6) (push) Has been cancelled
Build Kernel - Android 15 / build-kernel (2024-10, 50, 6.6) (push) Has been cancelled
Build Kernel - Android 15 / build-kernel (2024-11, 56, 6.6) (push) Has been cancelled
Build Kernel - Android 15 / build-kernel (2024-12, 57, 6.6) (push) Has been cancelled
Build Kernel - Android 15 / check-build-kernel (2024-12, 57, 6.6) (push) Has been cancelled
Build Kernel - ChromeOS ARCVM / Build ChromeOS ARCVM kernel (arm64, build.config.gki.aarch64, arm64_arcvm_defconfig, Image) (push) Has been cancelled
Build Kernel - ChromeOS ARCVM / Build ChromeOS ARCVM kernel (x86_64, build.config.gki.x86_64, x86_64_arcvm_defconfig, bzImage) (push) Has been cancelled
Build Kernel - AVD / build-kernel (aarch64, android-15-avd_aarch64.xml, android-15-avd_aarch64) (push) Has been cancelled
Build Kernel - AVD / build-kernel (x86_64, android-14-avd_x86_64.xml, android-14-avd_x86_64) (push) Has been cancelled
Build Kernel - AVD / build-kernel (x86_64, android-15-avd_x86_64.xml, android-15-avd_x86_64) (push) Has been cancelled
Build Kernel - WSA / build (arm64, 5.15.104.1) (push) Has been cancelled
Build Kernel - WSA / build (arm64, 5.15.104.2) (push) Has been cancelled
Build Kernel - WSA / build (arm64, 5.15.104.3) (push) Has been cancelled
Build Kernel - WSA / build (arm64, 5.15.104.4) (push) Has been cancelled
Build Kernel - WSA / build (arm64, 5.15.94.2) (push) Has been cancelled
Build Kernel - WSA / build (x86_64, 5.15.104.1) (push) Has been cancelled
Build Kernel - WSA / build (x86_64, 5.15.104.2) (push) Has been cancelled
Build Kernel - WSA / build (x86_64, 5.15.104.3) (push) Has been cancelled
Build Kernel - WSA / build (x86_64, 5.15.104.4) (push) Has been cancelled
Build Kernel - WSA / build (x86_64, 5.15.94.2) (push) Has been cancelled
Build Kernel - WSA / check_build (arm64) (push) Has been cancelled
Build Kernel - WSA / check_build (x86_64) (push) Has been cancelled
Build Kernel - Android 12 / upload-artifacts (push) Has been cancelled
Build Kernel - Android 13 / upload-artifacts (push) Has been cancelled
Build Kernel - Android 14 / upload-artifacts (push) Has been cancelled
Build Kernel - Android 15 / upload-artifacts (push) Has been cancelled
Allow toggling unmount setting for all apps (#2450)
This fixes https://github.com/tiann/KernelSU/issues/2416.
2025-02-18 11:07:52 +08:00

526 lines
13 KiB
C

#include <linux/capability.h>
#include <linux/compiler.h>
#include <linux/fs.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/compiler_types.h>
#include "ksu.h"
#include "klog.h" // IWYU pragma: keep
#include "selinux/selinux.h"
#include "kernel_compat.h"
#include "allowlist.h"
#include "manager.h"
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
#define FILE_FORMAT_VERSION 3 // u32
#define KSU_APP_PROFILE_PRESERVE_UID 9999 // NOBODY_UID
#define KSU_DEFAULT_SELINUX_DOMAIN "u:r:su:s0"
static DEFINE_MUTEX(allowlist_mutex);
// default profiles, these may be used frequently, so we cache it
static struct root_profile default_root_profile;
static struct non_root_profile default_non_root_profile;
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly __aligned(PAGE_SIZE);
static int allow_list_pointer __read_mostly = 0;
static void remove_uid_from_arr(uid_t uid)
{
int *temp_arr;
int i, j;
if (allow_list_pointer == 0)
return;
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
if (temp_arr == NULL) {
pr_err("%s: unable to allocate memory\n", __func__);
return;
}
for (i = j = 0; i < allow_list_pointer; i++) {
if (allow_list_arr[i] == uid)
continue;
temp_arr[j++] = allow_list_arr[i];
}
allow_list_pointer = j;
for (; j < ARRAY_SIZE(allow_list_arr); j++)
temp_arr[j] = -1;
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
kfree(temp_arr);
}
static void init_default_profiles()
{
kernel_cap_t full_cap = CAP_FULL_SET;
default_root_profile.uid = 0;
default_root_profile.gid = 0;
default_root_profile.groups_count = 1;
default_root_profile.groups[0] = 0;
memcpy(&default_root_profile.capabilities.effective, &full_cap,
sizeof(default_root_profile.capabilities.effective));
default_root_profile.namespaces = 0;
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
// This means that we will umount modules by default!
default_non_root_profile.umount_modules = true;
}
struct perm_data {
struct list_head list;
struct app_profile profile;
};
static struct list_head allow_list;
static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
#define BITMAP_UID_MAX ((sizeof(allow_list_bitmap) * BITS_PER_BYTE) - 1)
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
static struct work_struct ksu_save_work;
static struct work_struct ksu_load_work;
bool persistent_allow_list(void);
void ksu_show_allow_list(void)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
pr_info("ksu_show_allow_list\n");
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
p->profile.allow_su);
}
}
#ifdef CONFIG_KSU_DEBUG
static void ksu_grant_root_to_shell()
{
struct app_profile profile = {
.allow_su = true,
.current_uid = 2000,
};
strcpy(profile.key, "com.android.shell");
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
ksu_set_app_profile(&profile, false);
}
#endif
bool ksu_get_app_profile(struct app_profile *profile)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool found = false;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
bool uid_match = profile->current_uid == p->profile.current_uid;
if (uid_match) {
// found it, override it with ours
memcpy(profile, &p->profile, sizeof(*profile));
found = true;
goto exit;
}
}
exit:
return found;
}
static inline bool forbid_system_uid(uid_t uid) {
#define SHELL_UID 2000
#define SYSTEM_UID 1000
return uid < SHELL_UID && uid != SYSTEM_UID;
}
static bool profile_valid(struct app_profile *profile)
{
if (!profile) {
return false;
}
if (profile->version < KSU_APP_PROFILE_VER) {
pr_info("Unsupported profile version: %d\n", profile->version);
return false;
}
if (profile->allow_su) {
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
return false;
}
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
return false;
}
}
return true;
}
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool result = false;
if (!profile_valid(profile)) {
pr_err("Failed to set app profile: invalid profile!\n");
return false;
}
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
// both uid and package must match, otherwise it will break multiple package with different user id
if (profile->current_uid == p->profile.current_uid &&
!strcmp(profile->key, p->profile.key)) {
// found it, just override it all!
memcpy(&p->profile, profile, sizeof(*profile));
result = true;
goto out;
}
}
// not found, alloc a new node!
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
if (!p) {
pr_err("ksu_set_app_profile alloc failed\n");
return false;
}
memcpy(&p->profile, profile, sizeof(*profile));
if (profile->allow_su) {
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
profile->key, profile->current_uid,
profile->rp_config.profile.gid,
profile->rp_config.profile.selinux_domain);
} else {
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
profile->key, profile->current_uid,
profile->nrp_config.profile.umount_modules);
}
list_add_tail(&p->list, &allow_list);
out:
if (profile->current_uid <= BITMAP_UID_MAX) {
if (profile->allow_su)
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
else
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
} else {
if (profile->allow_su) {
/*
* 1024 apps with uid higher than BITMAP_UID_MAX
* registered to request superuser?
*/
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
pr_err("too many apps registered\n");
WARN_ON(1);
return false;
}
allow_list_arr[allow_list_pointer++] = profile->current_uid;
} else {
remove_uid_from_arr(profile->current_uid);
}
}
result = true;
// check if the default profiles is changed, cache it to a single struct to accelerate access.
if (unlikely(!strcmp(profile->key, "$"))) {
// set default non root profile
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
sizeof(default_non_root_profile));
}
if (unlikely(!strcmp(profile->key, "#"))) {
// set default root profile
memcpy(&default_root_profile, &profile->rp_config.profile,
sizeof(default_root_profile));
}
if (persist)
persistent_allow_list();
return result;
}
bool __ksu_is_allow_uid(uid_t uid)
{
int i;
if (unlikely(uid == 0)) {
// already root, but only allow our domain.
return is_ksu_domain();
}
if (forbid_system_uid(uid)) {
// do not bother going through the list if it's system
return false;
}
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
// manager is always allowed!
return true;
}
if (likely(uid <= BITMAP_UID_MAX)) {
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
} else {
for (i = 0; i < allow_list_pointer; i++) {
if (allow_list_arr[i] == uid)
return true;
}
}
return false;
}
bool ksu_uid_should_umount(uid_t uid)
{
struct app_profile profile = { .current_uid = uid };
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
// we should not umount on manager!
return false;
}
bool found = ksu_get_app_profile(&profile);
if (!found) {
// no app profile found, it must be non root app
return default_non_root_profile.umount_modules;
}
if (profile.allow_su) {
// if found and it is granted to su, we shouldn't umount for it
return false;
} else {
// found an app profile
if (profile.nrp_config.use_default) {
return default_non_root_profile.umount_modules;
} else {
return profile.nrp_config.profile.umount_modules;
}
}
}
struct root_profile *ksu_get_root_profile(uid_t uid)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
if (uid == p->profile.current_uid && p->profile.allow_su) {
if (!p->profile.rp_config.use_default) {
return &p->profile.rp_config.profile;
}
}
}
// use default profile
return &default_root_profile;
}
bool ksu_get_allow_list(int *array, int *length, bool allow)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
int i = 0;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
if (p->profile.allow_su == allow) {
array[i++] = p->profile.current_uid;
}
}
*length = i;
return true;
}
void do_save_allow_list(struct work_struct *work)
{
u32 magic = FILE_MAGIC;
u32 version = FILE_FORMAT_VERSION;
struct perm_data *p = NULL;
struct list_head *pos = NULL;
loff_t off = 0;
struct file *fp =
ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
return;
}
// store magic and version
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic)) {
pr_err("save_allow_list write magic failed.\n");
goto exit;
}
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
sizeof(version)) {
pr_err("save_allow_list write version failed.\n");
goto exit;
}
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
p->profile.key, p->profile.current_uid,
p->profile.allow_su);
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
&off);
}
exit:
filp_close(fp, 0);
}
void do_load_allow_list(struct work_struct *work)
{
loff_t off = 0;
ssize_t ret = 0;
struct file *fp = NULL;
u32 magic;
u32 version;
#ifdef CONFIG_KSU_DEBUG
// always allow adb shell by default
ksu_grant_root_to_shell();
#endif
// load allowlist now!
fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
if (IS_ERR(fp)) {
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
return;
}
// verify magic
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic) ||
magic != FILE_MAGIC) {
pr_err("allowlist file invalid: %d!\n", magic);
goto exit;
}
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
sizeof(version)) {
pr_err("allowlist read version: %d failed\n", version);
goto exit;
}
pr_info("allowlist version: %d\n", version);
while (true) {
struct app_profile profile;
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
&off);
if (ret <= 0) {
pr_info("load_allow_list read err: %zd\n", ret);
break;
}
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
profile.key, profile.current_uid, profile.allow_su);
ksu_set_app_profile(&profile, false);
}
exit:
ksu_show_allow_list();
filp_close(fp, 0);
}
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data)
{
struct perm_data *np = NULL;
struct perm_data *n = NULL;
bool modified = false;
// TODO: use RCU!
mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) {
uid_t uid = np->profile.current_uid;
char *package = np->profile.key;
// we use this uid for special cases, don't prune it!
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
modified = true;
pr_info("prune uid: %d, package: %s\n", uid, package);
list_del(&np->list);
if (likely(uid <= BITMAP_UID_MAX)) {
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
}
remove_uid_from_arr(uid);
smp_mb();
kfree(np);
}
}
mutex_unlock(&allowlist_mutex);
if (modified) {
persistent_allow_list();
}
}
// make sure allow list works cross boot
bool persistent_allow_list(void)
{
return ksu_queue_work(&ksu_save_work);
}
bool ksu_load_allow_list(void)
{
return ksu_queue_work(&ksu_load_work);
}
void ksu_allowlist_init(void)
{
int i;
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
allow_list_arr[i] = -1;
INIT_LIST_HEAD(&allow_list);
INIT_WORK(&ksu_save_work, do_save_allow_list);
INIT_WORK(&ksu_load_work, do_load_allow_list);
init_default_profiles();
}
void ksu_allowlist_exit(void)
{
struct perm_data *np = NULL;
struct perm_data *n = NULL;
do_save_allow_list(NULL);
// free allowlist
mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) {
list_del(&np->list);
kfree(np);
}
mutex_unlock(&allowlist_mutex);
}