Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 100 additions & 70 deletions kernel/allowlist.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#include <linux/rcupdate.h>
#include <linux/limits.h>
#include <linux/rculist.h>
#include <linux/mutex.h>
#include <linux/task_work.h>
#include <linux/capability.h>
Expand All @@ -17,7 +20,6 @@
#include "selinux/selinux.h"
#include "allowlist.h"
#include "manager.h"
#include "syscall_hook_manager.h"
#include "su_mount_ns.h"

#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
Expand Down Expand Up @@ -84,6 +86,7 @@ static void init_default_profiles()

struct perm_data {
struct list_head list;
struct rcu_head rcu;
struct app_profile profile;
};

Expand All @@ -94,18 +97,18 @@ static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);

#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"

void persistent_allow_list(void);
void ksu_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);
rcu_read_lock();
list_for_each_entry_rcu (p, &allow_list, list) {
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
p->profile.allow_su);
}
rcu_read_unlock();
}

#ifdef CONFIG_KSU_DEBUG
Expand All @@ -126,11 +129,10 @@ static void ksu_grant_root_to_shell()
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);
rcu_read_lock();
list_for_each_entry_rcu (p, &allow_list, list) {
bool uid_match = profile->current_uid == p->profile.current_uid;
if (uid_match) {
// found it, override it with ours
Expand All @@ -141,6 +143,7 @@ bool ksu_get_app_profile(struct app_profile *profile)
}

exit:
rcu_read_unlock();
return found;
}

Expand Down Expand Up @@ -175,34 +178,50 @@ static bool profile_valid(struct app_profile *profile)
return true;
}

bool ksu_set_app_profile(struct app_profile *profile, bool persist)
int ksu_set_app_profile(struct app_profile *profile)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool result = false;
struct perm_data *p = NULL, *np;
int result = 0;
u16 count = 0;

if (!profile_valid(profile)) {
pr_err("Failed to set app profile: invalid profile!\n");
return false;
return -EINVAL;
}

list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
mutex_lock(&allowlist_mutex);

list_for_each_entry (p, &allow_list, list) {
++count;
// 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;
np = (struct perm_data *)kzalloc(sizeof(struct perm_data),
GFP_KERNEL);
if (!np) {
result = -ENOMEM;
goto out_unlock;
}
memcpy(&np->profile, profile, sizeof(*profile));
list_replace_rcu(&p->list, &np->list);
kfree_rcu(p, rcu);
goto out;
}
}

if (unlikely(count == U16_MAX)) {
pr_err("too many app profile\n");
result = -E2BIG;
goto out_unlock;
}

// not found, alloc a new node!
p = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL);
if (!p) {
pr_err("ksu_set_app_profile alloc failed\n");
return false;
result = -ENOMEM;
goto out_unlock;
}

memcpy(&p->profile, profile, sizeof(*profile));
Expand All @@ -216,10 +235,23 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
profile->key, profile->current_uid,
profile->nrp_config.profile.umount_modules);
}
list_add_tail(&p->list, &allow_list);

list_add_tail_rcu(&p->list, &allow_list);

out:
if (profile->current_uid <= BITMAP_UID_MAX) {
result = 0;

// 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));
} else if (unlikely(!strcmp(profile->key, "#"))) {
// set default root profile
// TODO: Do we really need this?
memcpy(&default_root_profile, &profile->rp_config.profile,
sizeof(default_root_profile));
} else 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);
Expand All @@ -235,34 +267,16 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
pr_err("too many apps registered\n");
WARN_ON(1);
return false;
} else {
allow_list_arr[allow_list_pointer++] = profile->current_uid;
}
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();
// FIXME: use a new flag
ksu_mark_running_process();
}

out_unlock:
mutex_unlock(&allowlist_mutex);
return result;
}

Expand Down Expand Up @@ -329,55 +343,72 @@ bool ksu_uid_should_umount(uid_t uid)
}
}

struct root_profile *ksu_get_root_profile(uid_t uid)
void ksu_get_root_profile(uid_t uid, struct root_profile *profile)
{
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 (is_uid_manager(uid)) {
goto use_default;
}

rcu_read_lock();
list_for_each_entry_rcu (p, &allow_list, list) {
if (uid == p->profile.current_uid && p->profile.allow_su) {
if (!p->profile.rp_config.use_default) {
return &p->profile.rp_config.profile;
memcpy(profile, &p->profile.rp_config.profile,
sizeof(*profile));
rcu_read_unlock();
return;
}
}
}
rcu_read_unlock();

use_default:
// use default profile
return &default_root_profile;
memcpy(profile, &default_root_profile, sizeof(*profile));
}

bool ksu_get_allow_list(int *array, int *length, bool allow)
bool ksu_get_allow_list(int *array, u16 length, u16 *out_length, u16 *out_total,
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);
u16 i = 0, j = 0;
rcu_read_lock();
list_for_each_entry_rcu (p, &allow_list, 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;
if (p->profile.allow_su == allow &&
!is_uid_manager(p->profile.current_uid)) {
if (j < length) {
array[j++] = p->profile.current_uid;
}
++i;
}
}
*length = i;
rcu_read_unlock();
if (out_length) {
*out_length = j;
}
if (out_total) {
*out_total = i;
}

return true;
}

// TODO: move to kernel thread or work queue
static void do_persistent_allow_list(struct callback_head *_cb)
{
u32 magic = FILE_MAGIC;
u32 version = FILE_FORMAT_VERSION;
struct perm_data *p = NULL;
struct list_head *pos = NULL;
loff_t off = 0;

mutex_lock(&allowlist_mutex);
struct file *fp =
filp_open(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));
goto unlock;
goto out;
}

// store magic and version
Expand All @@ -391,22 +422,22 @@ static void do_persistent_allow_list(struct callback_head *_cb)
goto close_file;
}

list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
mutex_lock(&allowlist_mutex);
list_for_each_entry (p, &allow_list, list) {
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
p->profile.key, p->profile.current_uid, p->profile.allow_su);

kernel_write(fp, &p->profile, sizeof(p->profile), &off);
}
mutex_unlock(&allowlist_mutex);

close_file:
filp_close(fp, 0);
unlock:
mutex_unlock(&allowlist_mutex);
out:
kfree(_cb);
}

void persistent_allow_list()
void ksu_persistent_allow_list()
{
struct task_struct *tsk;

Expand Down Expand Up @@ -475,7 +506,7 @@ void ksu_load_allow_list()

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);
ksu_set_app_profile(&profile);
}

exit:
Expand All @@ -495,7 +526,6 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
}

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;
Expand All @@ -505,20 +535,20 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
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);
list_del_rcu(&np->list);
kfree_rcu(np, rcu);
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();
smp_mb();
ksu_persistent_allow_list();
}
}

Expand Down
8 changes: 5 additions & 3 deletions kernel/allowlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,18 @@ bool __ksu_is_allow_uid_for_current(uid_t uid);
#define ksu_is_allow_uid_for_current(uid) \
unlikely(__ksu_is_allow_uid_for_current(uid))

bool ksu_get_allow_list(int *array, int *length, bool allow);
bool ksu_get_allow_list(int *array, u16 length, u16 *out_length, u16 *out_total,
bool allow);

void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *),
void *data);
void ksu_persistent_allow_list();

bool ksu_get_app_profile(struct app_profile *);
bool ksu_set_app_profile(struct app_profile *, bool persist);
int ksu_set_app_profile(struct app_profile *);

bool ksu_uid_should_umount(uid_t uid);
struct root_profile *ksu_get_root_profile(uid_t uid);
void ksu_get_root_profile(uid_t uid, struct root_profile *);

static inline bool is_appuid(uid_t uid)
{
Expand Down
Loading