kernel: baby version of profile

This commit is contained in:
weishu
2023-06-03 00:00:56 +08:00
parent 41265b0203
commit 3abb7e4ca2
4 changed files with 189 additions and 62 deletions

View File

@@ -1,5 +1,7 @@
#include "ksu.h"
#include "linux/delay.h" #include "linux/delay.h"
#include "linux/fs.h" #include "linux/fs.h"
#include "linux/gfp.h"
#include "linux/kernel.h" #include "linux/kernel.h"
#include "linux/list.h" #include "linux/list.h"
#include "linux/printk.h" #include "linux/printk.h"
@@ -8,16 +10,32 @@
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "selinux/selinux.h" #include "selinux/selinux.h"
#include "kernel_compat.h" #include "kernel_compat.h"
#include "allowlist.h"
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32 #define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
#define FILE_FORMAT_VERSION 1 // u32 #define FILE_FORMAT_VERSION 2 // u32
static DEFINE_MUTEX(allowlist_mutex); static DEFINE_MUTEX(allowlist_mutex);
// default root identify
static struct root_identity default_root_identity;
static bool default_umount_modules = true;
static void init_root_identity()
{
default_root_identity.uid = 0;
default_root_identity.gid = 0;
default_root_identity.groups_count = 1;
default_root_identity.groups[0] = 0;
memset(&default_root_identity.capabilities, 0xff,
sizeof(default_root_identity.capabilities));
default_root_identity.namespaces = 0;
strcpy(default_root_identity.selinux_domain, "su");
}
struct perm_data { struct perm_data {
struct list_head list; struct list_head list;
uid_t uid; struct app_profile profile;
bool allow;
}; };
static struct list_head allow_list; static struct list_head allow_list;
@@ -36,20 +54,65 @@ void ksu_show_allow_list(void)
pr_info("ksu_show_allow_list"); pr_info("ksu_show_allow_list");
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
pr_info("uid :%d, allow: %d\n", p->uid, p->allow); pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
p->profile.allow_su);
} }
} }
bool ksu_allow_uid(uid_t uid, bool allow, bool persist) static void ksu_grant_root_to_shell()
{
struct app_profile profile = {
.allow_su = true,
.current_uid = 2000,
};
strcpy(profile.key, "com.android.shell");
ksu_set_app_profile(&profile, false);
}
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);
if (!strcmp(profile->key, p->profile.key)) {
// found it, override it with ours
memcpy(profile, &p->profile, sizeof(*profile));
found = true;
goto exit;
}
}
if (!found) {
// don't found, fill it with default profile
if (profile->allow_su) {
profile->root_profile.use_default = true;
memcpy(&profile->root_profile.identity,
&default_root_identity,
sizeof(default_root_identity));
} else {
profile->non_root_profile.use_default = true;
profile->non_root_profile.umount_modules =
default_umount_modules;
}
}
exit:
return found;
}
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
{ {
// find the node first!
struct perm_data *p = NULL; struct perm_data *p = NULL;
struct list_head *pos = NULL; struct list_head *pos = NULL;
bool result = false; bool result = false;
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
if (uid == p->uid) { if (!strcmp(profile->key, p->profile.key)) {
p->allow = allow; // found it, just override it all!
memcpy(&p->profile, profile, sizeof(*profile));
result = true; result = true;
goto exit; goto exit;
} }
@@ -58,14 +121,13 @@ bool ksu_allow_uid(uid_t uid, bool allow, bool persist)
// not found, alloc a new node! // not found, alloc a new node!
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL); p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
if (!p) { if (!p) {
pr_err("alloc allow node failed.\n"); pr_err("ksu_set_app_profile alloc failed\n");
return false; return false;
} }
p->uid = uid;
p->allow = allow;
pr_info("allow_uid: %d, allow: %d", uid, allow);
memcpy(&p->profile, profile, sizeof(*profile));
pr_info("set app profile, key: %s, uid: %d\n", profile->key,
profile->current_uid);
list_add_tail(&p->list, &allow_list); list_add_tail(&p->list, &allow_list);
result = true; result = true;
@@ -89,8 +151,8 @@ bool ksu_is_allow_uid(uid_t uid)
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
// pr_info("is_allow_uid uid :%d, allow: %d\n", p->uid, p->allow); // pr_info("is_allow_uid uid :%d, allow: %d\n", p->uid, p->allow);
if (uid == p->uid) { if (uid == p->profile.current_uid) {
return p->allow; return p->profile.allow_su;
} }
} }
@@ -105,8 +167,8 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow); // pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
if (p->allow == allow) { if (p->profile.allow_su == allow) {
array[i++] = p->uid; array[i++] = p->profile.allow_su;
} }
} }
*length = i; *length = i;
@@ -114,7 +176,7 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
return true; return true;
} }
void do_persistent_allow_list(struct work_struct *work) void do_save_allow_list(struct work_struct *work)
{ {
u32 magic = FILE_MAGIC; u32 magic = FILE_MAGIC;
u32 version = FILE_FORMAT_VERSION; u32 version = FILE_FORMAT_VERSION;
@@ -131,7 +193,8 @@ void do_persistent_allow_list(struct work_struct *work)
} }
// store magic and version // store magic and version
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) { if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic)) {
pr_err("save_allow_list write magic failed.\n"); pr_err("save_allow_list write magic failed.\n");
goto exit; goto exit;
} }
@@ -144,10 +207,12 @@ void do_persistent_allow_list(struct work_struct *work)
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
pr_info("save allow list uid :%d, allow: %d\n", p->uid, pr_info("save allow list, name: %s uid :%d, allow: %d\n",
p->allow); p->profile.key, p->profile.current_uid,
ksu_kernel_write_compat(fp, &p->uid, sizeof(p->uid), &off); p->profile.allow_su);
ksu_kernel_write_compat(fp, &p->allow, sizeof(p->allow), &off);
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
&off);
} }
exit: exit:
@@ -163,27 +228,21 @@ void do_load_allow_list(struct work_struct *work)
u32 version; u32 version;
KWORKER_INSTALL_KEYRING(); KWORKER_INSTALL_KEYRING();
#ifdef CONFIG_KSU_DEBUG
// always allow adb shell by default
ksu_grant_root_to_shell();
#endif
// load allowlist now! // load allowlist now!
fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0); fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
#ifdef CONFIG_KSU_DEBUG
int errno = PTR_ERR(fp);
if (errno == -ENOENT) {
ksu_allow_uid(2000, true,
true); // allow adb shell by default
} else {
pr_err("load_allow_list open file failed: %ld\n",
PTR_ERR(fp));
}
#else
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp)); pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
#endif
return; return;
} }
// verify magic // verify magic
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) || if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic) ||
magic != FILE_MAGIC) { magic != FILE_MAGIC) {
pr_err("allowlist file invalid: %d!\n", magic); pr_err("allowlist file invalid: %d!\n", magic);
goto exit; goto exit;
@@ -198,18 +257,19 @@ void do_load_allow_list(struct work_struct *work)
pr_info("allowlist version: %d\n", version); pr_info("allowlist version: %d\n", version);
while (true) { while (true) {
u32 uid; struct app_profile profile;
bool allow = false;
ret = ksu_kernel_read_compat(fp, &uid, sizeof(uid), &off); ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
&off);
if (ret <= 0) { if (ret <= 0) {
pr_info("load_allow_list read err: %zd\n", ret); pr_info("load_allow_list read err: %zd\n", ret);
break; break;
} }
ret = ksu_kernel_read_compat(fp, &allow, sizeof(allow), &off);
pr_info("load_allow_uid: %d, allow: %d\n", uid, allow); pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
profile.key, profile.current_uid, profile.allow_su);
ksu_allow_uid(uid, allow, false); ksu_set_app_profile(&profile, false);
} }
exit: exit:
@@ -226,7 +286,7 @@ void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data)
// TODO: use RCU! // TODO: use RCU!
mutex_lock(&allowlist_mutex); mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) { list_for_each_entry_safe (np, n, &allow_list, list) {
uid_t uid = np->uid; uid_t uid = np->profile.current_uid;
if (!is_uid_exist(uid, data)) { if (!is_uid_exist(uid, data)) {
modified = true; modified = true;
pr_info("prune uid: %d\n", uid); pr_info("prune uid: %d\n", uid);
@@ -256,8 +316,11 @@ void ksu_allowlist_init(void)
{ {
INIT_LIST_HEAD(&allow_list); INIT_LIST_HEAD(&allow_list);
INIT_WORK(&ksu_save_work, do_persistent_allow_list); INIT_WORK(&ksu_save_work, do_save_allow_list);
INIT_WORK(&ksu_load_work, do_load_allow_list); INIT_WORK(&ksu_load_work, do_load_allow_list);
// init default_root_identity, which is used for root identity when root profile is not set.
init_root_identity();
} }
void ksu_allowlist_exit(void) void ksu_allowlist_exit(void)
@@ -265,7 +328,7 @@ void ksu_allowlist_exit(void)
struct perm_data *np = NULL; struct perm_data *np = NULL;
struct perm_data *n = NULL; struct perm_data *n = NULL;
do_persistent_allow_list(NULL); do_save_allow_list(NULL);
// free allowlist // free allowlist
mutex_lock(&allowlist_mutex); mutex_lock(&allowlist_mutex);

View File

@@ -2,6 +2,7 @@
#define __KSU_H_ALLOWLIST #define __KSU_H_ALLOWLIST
#include "linux/types.h" #include "linux/types.h"
#include "ksu.h"
void ksu_allowlist_init(void); void ksu_allowlist_init(void);
@@ -13,10 +14,10 @@ void ksu_show_allow_list(void);
bool ksu_is_allow_uid(uid_t uid); bool ksu_is_allow_uid(uid_t uid);
bool ksu_allow_uid(uid_t uid, bool allow, bool persist);
bool ksu_get_allow_list(int *array, int *length, bool allow); bool ksu_get_allow_list(int *array, int *length, bool allow);
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data); void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data);
bool ksu_get_app_profile(struct app_profile *);
bool ksu_set_app_profile(struct app_profile *, bool persist);
#endif #endif

View File

@@ -186,7 +186,8 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
if (userId == 0) { if (userId == 0) {
prefix = "/data/data"; prefix = "/data/data";
} else { } else {
snprintf(prefixTmp, sizeof(prefixTmp), "/data/user/%d", userId); snprintf(prefixTmp, sizeof(prefixTmp), "/data/user/%d",
userId);
prefix = prefixTmp; prefix = prefixTmp;
} }
@@ -227,10 +228,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("grant_root: prctl reply error\n"); pr_err("grant_root: prctl reply error\n");
} }
} else {
pr_info("deny root for: %d\n", current_uid());
// add it to deny list!
ksu_allow_uid(current_uid().val, false, true);
} }
return 0; return 0;
} }
@@ -332,17 +329,37 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
} }
// we are already manager // we are already manager
if (arg2 == CMD_ALLOW_SU || arg2 == CMD_DENY_SU) { if (arg2 == CMD_GET_APP_PROFILE) {
bool allow = arg2 == CMD_ALLOW_SU; struct app_profile profile;
bool success = false; if (copy_from_user(&profile, arg3, sizeof(profile))) {
uid_t uid = (uid_t)arg3; pr_err("copy profile failed\n");
success = ksu_allow_uid(uid, allow, true); return 0;
if (success) { }
ksu_get_app_profile(&profile);
if (copy_to_user(arg3, &profile, sizeof(profile))) {
pr_err("copy profile failed\n");
return 0;
}
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n", arg2);
}
return 0;
}
if (arg2 == CMD_SET_APP_PROFILE) {
struct app_profile profile;
if (copy_from_user(&profile, arg3, sizeof(profile))) {
pr_err("copy profile failed\n");
return 0;
}
// todo: validate the params
if (ksu_set_app_profile(&profile, true)) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("prctl reply error, cmd: %d\n", arg2); pr_err("prctl reply error, cmd: %d\n", arg2);
} }
} }
ksu_show_allow_list();
return 0; return 0;
} }
@@ -366,7 +383,8 @@ static bool should_umount(struct path *path)
} }
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) { if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
pr_info("ignore global mnt namespace process: %d\n", current_uid().val); pr_info("ignore global mnt namespace process: %d\n",
current_uid().val);
return false; return false;
} }

View File

@@ -1,13 +1,17 @@
#ifndef __KSU_H_KSU #ifndef __KSU_H_KSU
#define __KSU_H_KSU #define __KSU_H_KSU
#include "linux/capability.h"
#include "linux/workqueue.h" #include "linux/workqueue.h"
#ifndef KSU_GIT_VERSION #ifndef KSU_GIT_VERSION
#warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!" #warning \
"KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!"
#define KERNEL_SU_VERSION (16) #define KERNEL_SU_VERSION (16)
#else #else
#define KERNEL_SU_VERSION (10000 + KSU_GIT_VERSION + 200) // major * 10000 + git version + 200 for historical reasons #define KERNEL_SU_VERSION \
(10000 + KSU_GIT_VERSION + \
200) // major * 10000 + git version + 200 for historical reasons
#endif #endif
#define KERNEL_SU_OPTION 0xDEADBEEF #define KERNEL_SU_OPTION 0xDEADBEEF
@@ -22,10 +26,51 @@
#define CMD_REPORT_EVENT 7 #define CMD_REPORT_EVENT 7
#define CMD_SET_SEPOLICY 8 #define CMD_SET_SEPOLICY 8
#define CMD_CHECK_SAFEMODE 9 #define CMD_CHECK_SAFEMODE 9
#define CMD_GET_APP_PROFILE 10
#define CMD_SET_APP_PROFILE 11
#define EVENT_POST_FS_DATA 1 #define EVENT_POST_FS_DATA 1
#define EVENT_BOOT_COMPLETED 2 #define EVENT_BOOT_COMPLETED 2
#define KSU_MAX_PACKAGE_NAME 256
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
#define KSU_MAX_GROUPS 32
#define KSU_SELINUX_DOMAIN 64
struct root_identity {
int32_t uid;
int32_t gid;
int32_t groups[KSU_MAX_GROUPS];
int32_t groups_count;
kernel_cap_t capabilities;
char selinux_domain[KSU_SELINUX_DOMAIN];
int32_t namespaces;
};
struct app_profile {
// this is usually the package of the app, but can be other value for special apps
char key[KSU_MAX_PACKAGE_NAME];
int32_t current_uid;
bool allow_su;
union {
struct {
bool use_default;
char template_name[KSU_MAX_PACKAGE_NAME];
struct root_identity identity;
} root_profile;
struct {
bool use_default;
bool umount_modules;
} non_root_profile;
};
};
bool ksu_queue_work(struct work_struct *work); bool ksu_queue_work(struct work_struct *work);
static inline int startswith(char *s, char *prefix) static inline int startswith(char *s, char *prefix)